什么是代理模式?
代理模式分为静态代理和动态代理,他们之间的区别是什么?
通过学习了解到动态代理实际上是一种符合AOP设计思想的技术,那么AOP中的动态代理是什么?
此外,我们发现代理模式和装饰者模式很像,那么他们之间的区别是什么?
代理模式是23种设计模式种的一种。 按照代理的创建时期可以将代理类分为两种:动态代理和静态代理。目前动态代理中常见的两种是JDK动态代理和cglib动态代理。
一、静态代理
二、动态代理
要是想实现动态代理,需要解决如下两个问题:
问题1: 如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
问题2: 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
1. JDK动态代理
jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方
首先创建一个接口,代理类和被代理类共同实现这个接口
interface Human {
String getBelief();
void eat(String food);
}
创建被代理类
Class SuperMan implements HUman{
@Override
public String getBelief(){
return "I believe I can Fly!"
}
@Override
public void eat(String food){
Systemn.out.println("我喜欢吃"+food);
}
}
解决问题一:获取到代理对象使用newProxyInstance 方法,返回的是一个代理对象
第一个参数:是当前这个对象是哪个类加载器加载的,就填写哪个,一般都写成 obj.getClass().getClassLoader()
第二个参数:这里我们要创建的是代理类对象,而代理类对象和被代理类对象要实现同样的接口,因此这里传入的是接口
第三个参数:InvocationHandler,这个是一个接口,因此我们要传入她的这个接口的实现类对象。InvocationHandler这个接口,涉及到我们所说的第二个问题,即如何动态的去调用被代理对象中的同名方法。
//代理工厂
Class ProxyFactory {
//获取被代理对象
public static Object getProxyInstance(Object obj){
//MyInvocationHandler实例
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj)
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler)
}
@Override
public String getBelief(){
return "I believe I can Fly!"
}
@Override
public void eat(String food){
Systemn.out.println("我喜欢吃"+food);
}
}
InvocationHandler接口实现
invoke方法作用
invoke 当我们通过代理类的对象,调用方法a时,就会自动的调用这个invoke方法,然后将a方法作为参数传入进去。那为什么会这样呢?因为我们在上面生成代理类对象的时候,传递过InvocationHandler这个参数,然后就会自动去找这个参数中的invoke方法。
invoke方法的第一个参数,是代理类对象,第二个参数method,就是代理类对象要调用的方法,我们的目的是:代理类调用哪个方法,相应的被代理类中的哪个方法就被调用
将被代理类要执行的方法a的功能声明在invoke的方法中
class MyInvocationHandler implements InvocationHandler{
private Object obj; //被代理类对象
//对被代理类对象赋值
public void bind(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args){
//被代理类对象的方法执行
ret=method.invoke(obj, args);
}
}
执行
SuperMan superman = new SuperMan(); //生成被代理对象
//生成代理对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superman);
//通过代理对象调用eat方法
proxyInstance.eat("四川麻辣烫");
2. CGlib动态代理
三、代理模式和装饰者模式的区别
四、AOP面相切面编程
AOP原理: 将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决,采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能。所谓面向切面编程,是一种通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序 动态添加功能的技术。
AOP(Aspect Oriented Programming)中的一些术语:
- 通知(Advice)就是想要加入的功能,提前定义好,然后在想用的地方用一下
- 连接点(JoinPoint)就是Spring允许使用通知的地方,在Spring中之支持方法连接点。在一个类中,有15个方法,那就有15个连接点
*切入点(PointCut)在任何连接点中都可以放入我们想要的功能,但是我们只需要在某几个连接点中放入我们事先定义好的功能,因此放入通知的地方叫做切入点。或者说 只有放入通知的连接点叫做 切入点 - 切面(Aspect)切面是通知和切入点的结合。
- 目标(Target)。
- 织入(Weaving)
Advice的类型:
- before advice 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
- after return advice 在一个 join point 正常返回后执行的 advice
- after throwing advice 当一个 join point 抛出异常后执行的 advice
- after(final) advice 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
- around advice 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
- introductionintroduction可以为原有的对象增加新的属性和方法。
AOP 环境配置
导包:
spring-aop-4.3.25.RELEASE.jar
spring-aspects-4.3.25.RELEASE.jar
aspectjweaver-1.87.jar
aopalliance-1.0.jar
首先定义增强处理的类
然后配置增强处理类和配置切面:在核心配置文件applicationContext中
在切面中引入增强处理类和配置通知的类型,其中method属性是告诉框架在增强处理类中哪个是要前置增强的方法,哪个是后置增强的方法。pointcut-ref属性是在哪个切点进行使用。returning属性是在后置增强的时候将结果直接注入到这个参数中。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置增强处理类IoC -->
<bean id="loggerIncrease" class="cn.kgc.demo3.increse.LoggingIncrease"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 切点(给哪些功能增强处理) -->
<aop:pointcut expression="execution(* cn.kgc.demo3.service..*.*(..))" id="myPointCut"/>
<!-- 配置切面 -->
<aop:aspect ref="loggerIncrease">
<!-- 前置增强
method:增强处理类中的前置增强方法
pointcut-ref:引用切点
-->
<aop:before method="beforeMethod" pointcut-ref="myPointCut"/>
<!-- 后置增强
returning:返回值
-->
<aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
切入点表达式:
public * addNewUser(entity.User) 表示对所有名字为addNewUser的,且参数是User对象的方法 进行增强
public void * (entity.User) 表示对返回值为void 参数是User的方法进行增强
public void addNewUser(…) 表示对返回值为void和方法名为addNewUser的方法进行增强
* com.service.* . * (…) 这个service包下面的所有类的所有方法
* com.service…* . * (…) service包及其子包下的所有类的所有方法
AOP注解
- @ Aspect切面
- @Before前置增强
- @AfterReturning后置增强
开启注释驱动
<context:component-scan base-package="com.ctc" />//IOC自动扫包
<aop:aspectj-autoproxy />//使用AOP注解
增强处理类
使用@Component注解,是因为在XML配置中,需要将增强处理类交给Spring
@Aspect注解,相当于<aop:aspect ref=“增强处理类”>
在增强处理类的方法中使用@Before等注解,@Before(“切入点表达式”)
@Aspect
@Component
public class LogAdvice {
@Before("com.ctc.AspectJ.UserAspect.addLog()")
public void before(){
System.out.println("LogAdvice before advice ");
}
@AfterReturning(pointcut="com.ctc.AspectJ.UserAspect.addLog()",returning="result")
public void AfterReturning(){
System.out.println("LogAdvice after returning advice ");
}
}
@Pointcut注解
上面每个增强处理类中的方法上的切入点表达式子相同,因此可以使用@Pointcut注解。在增强处理类中随便定义一个空方法,然后使用@Pointcut注解进行修饰
@Aspect
@Component
public class LogAdvice {
//匹配所有ServiceImpl包下面的所有类的所有方法
@Pointcut("execution(* com.ctc.ServiceImpl.*.*(..))")
public void addLog(){}
@Before("com.ctc.AspectJ.UserAspect.addLog()")
public void before("addLog()"){
System.out.println("LogAdvice before advice ");
}
@AfterReturning()
public void AfterReturning(pointcut="addLog()",returning="result"){
System.out.println("LogAdvice after returning advice ");
}
}