Spring之静态代理、动态代理(实现动态代理的两种方式)与AOP底层实现动态代理(两种方式)

it2023-08-08  80

Spring之静态代理、动态代理(实现动态代理的两种方式)与AOP底层实现动态代理(两种方式)

2-1代理

2.1.1 静态代理

抽象角色:一般是接口或者抽象类真实角色:也就是目标对象 被代理的角色代理角色:代理目标对象 除了实现目标对象中的功能外 还可以额外扩展其它功能客户角色:使用代理角色完成其需要执行的功能

静态代理的好处:

可以使我们的目标对象(真实角色)功能更加纯粹 不需要关注于其它事务公共的业务由代理来完成 实现了业务的分工公共业务需要扩展时更加方便 减少了代码的冗余性

2.1.2 静态代理的实现

2.1.2.1 抽象角色

UserDao接口

public interface UserDao { void add(); void delete(); void update(); void query(); }

2.1.2.2 目标对象(真实角色)

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()方法"); } }

2.1.2.3 代理角色

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("我正在检查安全性....."); } }

2.1.2.3 测试类进行测试

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()方法

没问题,说明静态代理验证成功 除了实现真实对象中的功能 还能额外添加新的功能

但是静态代理有一定的缺陷:

静态代理需要代理类与目标类实现同样的接口,即如果想实现代理,则会多出一个与实现类(目标类)相似的类,如果程序中多处需要使用代理的话,就会多出许多这种多余的类,导致程序中类过多 多了代理类 , 工作量变大了 .开发效率降低

2-2 动态代理

动态代理的角色和静态代理的一样 .动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

实现动态代理的方式

JDK实现动态代理 ------------------------基于接口的动态代理cglib实现动态代理-----------------------基于类的动态代理现在常用的使用动态代理的方式为 javasist

2.2.1动态代理的实现方式一: JDK实现动态代理(实现接口)

使用JDK实现动态代理需要了解的两个类

InvocationHandler:接口 提供了invoke方法 当代理实例调用invoke方法时 可以调用目标对象中的方法Proxy :创建动态代理或者静态代理对象

2.2.1.1 定义UserJDKDynamicProxy类

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("需要进行安全性检查..."); } }

2.2.1.2测试类实现

private UserJDKDynamicProxy dynamicProxy; private UserDao userDao; @Before public void init(){ userDao = new UserDaoImpl(); dynamicProxy = new UserJDKDynamicProxy(userDao); } //2.测试JDK实现动态代理 @Test public void test02(){ UserDao proxyObject = dynamicProxy.createProxyObject(); proxyObject.add(); System.out.println("==============="); proxyObject.query(); }

测试结果

UserDao----->>>>执行了add()方法 =============== 我正在检查安全性..... UserDao----->>>>执行了query()方法

2.2.2 动态代理的实现方式二:cglib实现动态代理(继承类)

2.2.2.1 定义UserCglibDynamicProxy类

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("需要进行安全性检查..."); } }

2.2.2.2 测试类

private UserDao userDao; private UserCglibDynamicProxy cglibDynamicProxy; @Before public void init(){ userDao = new UserDaoImpl(); cglibDynamicProxy = new UserCglibDynamicProxy(); } //3.测试Cglib实现动态代理 @Test public void test03(){ UserDaoImpl proxyObject = cglibDynamicProxy.createProxyObject(); proxyObject.add(); System.out.println("------------"); proxyObject.query(); }

效果演示:

UserDao----->>>>执行了add()方法 ------------ 需要进行安全性检查... UserDao----->>>>执行了query()方法

动态代理的好处

可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .公共的业务由代理来完成 . 实现了业务的分工 ,公共业务发生扩展时变得更加集中和方便 .一个动态代理 一般处理一类业务一个动态代理可以处理多个子类 代理的是接口

2-3 spring之Aop

AOP的底层就是动态代理 主要思想就是:横向重复 纵向抽取(形成一个新的组件如Filter或Interceptor)

比如说:每一个servlet都需要进行编码格式的转换 这个时候如果不抽取出来 那么每一次servlet就需要写重复的代码 这时候就可以使用aop来抽取

只需要写一个过滤器就行 作用于每一个servlet

2.3.1 AoP名词

Joinpoint: 连接点 ----目标对象中 所有可增强的方法Pointcut 切入点------目标对象中 已经增强的方法Advice 通知/增强----增强的代码 也就是我们额外写的代码Target 目标对象 ------被代理的对象Weaving 织入 ---------------将通知应用到切入点的过程Proxy 代理 --------------将通知织入到目标对象之后 就形成了代理对象aspect 切面---------------切入点+通知

2.3.2需要导入的jar包主要有

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

2.3.3 实现Aop方式一:使用xml方式完成Aop功能

2.3.3.1 目标对象

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()方法"); } }

2.3.3.2 准备通知对象

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("无论是否出现异常,都会调用!"); } }

2.3.3.3 测试

package com.zelin.test; import com.zelin.dao.UserDao; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestAop { @Autowired private UserDao userDao; @Test public void test01(){ userDao.add(); } }

测试结果

前置通知. 环绕通知-前面代码 UserDao----->>>>执行了add()方法 环绕通知-后面代码 无论是否出现异常,都会调用! 后置通知,出现异常不调用.

2.3.3.4 applicationContext.xml配置文件

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--1.配置目标对象--> <bean id="userDao" class="com.zelin.dao.impl.UserDaoImpl"/> <!--2.配置通知对象--> <bean id="myAdvice" class="com.zelin.advice.MyAdvice"/> <!--3.配置aop--> <aop:config> <!--3.1)配置切入点--> <aop:pointcut id="myPC" expression="execution(* com.zelin.dao.impl.*DaoImpl.*(..))"/> <!--3.2)配置切面--> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut-ref="myPC"/> <aop:after-returning method="afterReturning" pointcut-ref="myPC"/> <aop:after-throwing method="afterThrowing" pointcut-ref="myPC"/> <aop:after method="after" pointcut-ref="myPC"/> <aop:around method="around" pointcut-ref="myPC"/> </aop:aspect> </aop:config> </beans>

2.3.4 实现Aop方式二:使用注解实现Aop功能

2.3.4.1 配置文件applicationContext2.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--1-配置扫描包--> <context:component-scan base-package="com.zelin"/> <!--2-配置切面自动生成代理对象--> <aop:aspectj-autoproxy/> </beans>

2.3.4.2 目标对象

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()方法"); } }

2.3.4.3 准备通知对象

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("无论是否出现异常,都会调用!"); } }

2.3.4.4 测试

package com.zelin.test; import com.zelin.dao.UserDao; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class TestAop2 { @Autowired private UserDao userDao; @Test public void test01(){ userDao.add(); } }

测试结果

环绕通知-前面代码 前置通知. UserDao----->>>>执行了add()方法 环绕通知-后面代码 无论是否出现异常,都会调用! 后置通知,出现异常不调用. 不得了了,出了异常了!

测试成功!

最新回复(0)