静态代理的好处:
可以使我们的目标对象(真实角色)功能更加纯粹 不需要关注于其它事务公共的业务由代理来完成 实现了业务的分工公共业务需要扩展时更加方便 减少了代码的冗余性UserDao接口
public interface UserDao { void add(); void delete(); void update(); void query(); }UserDaoImpl.java
package com.zelin.dao.impl; import com.zelin.dao.UserDao; /** * @author wf * @date 2020-10-20 16:06 */ public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("UserDao----->>>>执行了add()方法"); } @Override public void delete() { System.out.println("UserDao----->>>>执行了delete()方法"); } @Override public void update() { System.out.println("UserDao----->>>>执行了update()方法"); } @Override public void query() { System.out.println("UserDao----->>>>执行了query()方法"); } }ProxyStatic.java
package com.zelin.proxy; import com.zelin.dao.UserDao; import com.zelin.dao.impl.UserDaoImpl; /** * @author wf * @date 2020-10-20 16:08 */ public class ProxyStatic implements UserDao { private UserDao userDao; public ProxyStatic(UserDao userDao) { this.userDao = userDao; } @Override public void add() { userDao.add(); } @Override public void delete() { userDao.delete(); } @Override public void update() { userDao.update(); } @Override public void query() { safe(); userDao.query(); } //自定义方法 检查安全性 private void safe(){ System.out.println("我正在检查安全性....."); } }TestProxy.java
package com.zelin.test; import com.zelin.dao.UserDao; import com.zelin.dao.impl.UserDaoImpl; import com.zelin.proxy.ProxyStatic; import org.junit.Before; import org.junit.Test; /** * @author wf * @date 2020-10-20 16:10 */ public class TestProxy { private ProxyStatic proxyJDKStatic; private UserDao userDao; @Before public void init(){ userDao = new UserDaoImpl(); proxyJDKStatic = new ProxyStatic(userDao); } @Test public void test01(){ proxyJDKStatic.add(); System.out.println("================"); proxyJDKStatic.query(); } }实现结果
UserDao----->>>>执行了add()方法 ================ 我正在检查安全性..... UserDao----->>>>执行了query()方法没问题,说明静态代理验证成功 除了实现真实对象中的功能 还能额外添加新的功能
但是静态代理有一定的缺陷:
静态代理需要代理类与目标类实现同样的接口,即如果想实现代理,则会多出一个与实现类(目标类)相似的类,如果程序中多处需要使用代理的话,就会多出许多这种多余的类,导致程序中类过多 多了代理类 , 工作量变大了 .开发效率降低
实现动态代理的方式
JDK实现动态代理 ------------------------基于接口的动态代理cglib实现动态代理-----------------------基于类的动态代理现在常用的使用动态代理的方式为 javasist使用JDK实现动态代理需要了解的两个类
InvocationHandler:接口 提供了invoke方法 当代理实例调用invoke方法时 可以调用目标对象中的方法Proxy :创建动态代理或者静态代理对象UserJDKDynamicProxy.java
package com.zelin.proxy; import com.zelin.dao.UserDao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author wf * @date 2020-10-20 16:45 */ public class UserJDKDynamicProxy implements InvocationHandler { private UserDao userDao; public UserJDKDynamicProxy(UserDao userDao) { this.userDao = userDao; } //自定义创建代理对象 /** * 参数1:代表类加载器 * 参数2:代表目标对象所实现的接口类型 * 参数3:代表实现InvocationHandler接口的对象 * @return UserDao 目标对象 */ public UserDao createProxyObject(){ return (UserDao) Proxy.newProxyInstance(this.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //1.根据方法不同 执行不同的操作 //如果是调用查询query方法 就需要安全性检查 if(method.getName().equals("query")){ safe(); /** * 参数1:目标对象 而不是代理对象 * 参数2:目标对象对应的参数 */ return method.invoke(userDao,args); } return method.invoke(userDao,args); } private void safe(){ System.out.println("需要进行安全性检查..."); } }测试结果
UserDao----->>>>执行了add()方法 =============== 我正在检查安全性..... UserDao----->>>>执行了query()方法UserCglibDynamicProxy.java
package com.zelin.proxy; import com.zelin.dao.impl.UserDaoImpl; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author wf * @date 2020-10-20 17:01 */ public class UserCglibDynamicProxy implements MethodInterceptor { //1.构建代理对象 public UserDaoImpl createProxyObject(){ //1.1)得到Enhancer对象 Enhancer enhancer = new Enhancer(); //1.2)设置Enhancer的父对象 enhancer.setSuperclass(UserDaoImpl.class); //1.3)回调 enhancer.setCallback(this); //1.4)得到代理对象 UserDaoImpl userDao = (UserDaoImpl) enhancer.create(); //1.5)返回代理对象 return userDao; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //检查安全性 if(method.getName().equals("query")){ safe(); return methodProxy.invokeSuper(o,objects); } return methodProxy.invokeSuper(o,objects); } private void safe(){ System.out.println("需要进行安全性检查..."); } }效果演示:
UserDao----->>>>执行了add()方法 ------------ 需要进行安全性检查... UserDao----->>>>执行了query()方法动态代理的好处
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .公共的业务由代理来完成 . 实现了业务的分工 ,公共业务发生扩展时变得更加集中和方便 .一个动态代理 一般处理一类业务一个动态代理可以处理多个子类 代理的是接口AOP的底层就是动态代理 主要思想就是:横向重复 纵向抽取(形成一个新的组件如Filter或Interceptor)
比如说:每一个servlet都需要进行编码格式的转换 这个时候如果不抽取出来 那么每一次servlet就需要写重复的代码 这时候就可以使用aop来抽取
只需要写一个过滤器就行 作用于每一个servlet
spring-aop-4.2.4.RELEASE.jar
spring-aop-5.2.9.RELEASE.jar
spring-aspects-5.2.9.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
UserDaoImpl
package com.zelin.dao.impl; import com.zelin.dao.UserDao; /** * @author wf * @date 2020-10-20 16:06 */ public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("UserDao----->>>>执行了add()方法"); } @Override public void delete() { System.out.println("UserDao----->>>>执行了delete()方法"); } @Override public void update() { System.out.println("UserDao----->>>>执行了update()方法"); } @Override public void query() { System.out.println("UserDao----->>>>执行了query()方法"); } }MyAdvice.java
package com.zelin.advice; import org.aspectj.lang.ProceedingJoinPoint; //定义通知 public class MyAdvice { /** * 前置通知(Before):是调用方法之前调用 * 后置通知(AfterReturning):在调用方法之后调用(出现异常不调用 ) * 环绕通知(Around):在调用方法的前后,都会执行 * 异常通知(After-Throwing):在方法调用出现异常时执行 * 后置通知(After):无论是否出现异常都会调用 */ public void before(){ System.out.println("前置通知."); } public void afterReturning(){ System.out.println("后置通知,出现异常不调用."); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知-前面代码"); Object proceed = pjp.proceed(); System.out.println("环绕通知-后面代码"); return proceed; } public void afterThrowing(){ System.out.println("不得了了,出了异常了!"); } public void after(){ System.out.println("无论是否出现异常,都会调用!"); } }测试结果
前置通知. 环绕通知-前面代码 UserDao----->>>>执行了add()方法 环绕通知-后面代码 无论是否出现异常,都会调用! 后置通知,出现异常不调用.UserDaoImpl
package com.zelin.dao.impl; import com.zelin.dao.UserDao; import org.springframework.stereotype.Repository; @Repository public class UserDaoImpl implements UserDao { @Override public void add() { //int i = 10/0; System.out.println("UserDao----->>>>执行了add()方法"); } @Override public void delete() { System.out.println("UserDao----->>>>执行了delete()方法"); } @Override public void update() { System.out.println("UserDao----->>>>执行了update()方法"); } @Override public void query() { System.out.println("UserDao----->>>>执行了query()方法"); } }MyAdvice
package com.zelin.advice; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; //定义通知 @Aspect @Component public class MyAdvice { /** * 前置通知(Before):是调用方法之前调用 * 后置通知(AfterReturning):在调用方法之后调用(出现异常不调用 ) * 环绕通知(Around):在调用方法的前后,都会执行 * 异常通知(After-Throwing):在方法调用出现异常时执行 * 后置通知(After):无论是否出现异常都会调用 */ @Pointcut("execution(* com.zelin.dao.impl.*DaoImpl.*(..))") public void pc(){ } @Before("pc()") public void before(){ System.out.println("前置通知."); } @AfterReturning("pc()") public void afterReturning(){ System.out.println("后置通知,出现异常不调用."); } @Around("pc()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知-前面代码"); Object proceed = pjp.proceed(); System.out.println("环绕通知-后面代码"); return proceed; } @AfterReturning("pc()") public void afterThrowing(){ System.out.println("不得了了,出了异常了!"); } @After("pc()") public void after(){ System.out.println("无论是否出现异常,都会调用!"); } }测试结果
环绕通知-前面代码 前置通知. UserDao----->>>>执行了add()方法 环绕通知-后面代码 无论是否出现异常,都会调用! 后置通知,出现异常不调用. 不得了了,出了异常了!测试成功!
