一、动态代理
1、计算机中的代理:
①.可以想象一下鸡蛋是怎么回事,我们真正要调用的是蛋黄,因为我们不能直接调用,要通过鸡蛋壳才可以调用,蛋壳与蛋黄的命名方法一致,所以我们调用的是蛋壳上的方法,再通过鸡蛋壳调用蛋黄中真正工作的方法,返回给蛋壳然后再通过蛋壳返回到我们手上,可以想一下有中间商可以做很多小动作,就可以在中间加很多逻辑,通过蛋壳调用蛋黄的路上加入的逻辑,就叫前置通知,而通过蛋黄返回到蛋壳加入的逻辑叫后置通知,如果两个都加了则是循环通知。
这就是代理,想象一下鸡蛋怎么回事就好了
②.计算机中的代理示意图
2、动态代理
①.则想象,蛋壳上没有东西,通过动态代理的方式在蛋壳上动态的生成代理
3、基于JDK中动态代理实现
①.基于JDK中动态代理实现中有两重要的类:lnvocationHandler 、Proxy
lnvocationHandler
是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法。
Proxy
JDK中动态生成代理类的工具类
定义一个接口(基于JDK的动态代理只能使用接口)
public interface ISubject {
void hello(String param);
}
为接口定义实现类
public class SubjectImpl implements ISubject {
@Override
public void hello(String param) {
System.out.println("hello " + param);
}
}
实现一个代理类
public class JDKProxy implements InvocationHandler {
private Object target;
public JDKProxy(Object target) {
this.target = target;
}
public Object newProxy() {
return (ISubject)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------- 在业务方法调用之前可以进行前置增强 ------------");
//利用反射机制调用方法,invoke为返回值,如果没有返回null
Object invoke = method.invoke(target, args);
System.out.println("---------- 在业务方法调用之前可以进行后置增强 ------------");
return invoke;
}
}
写一个测试Demo
public class JDKProxy implements InvocationHandler {
private Object target;
public JDKProxy(Object target) {
this.target = target;
}
public Object newProxy() {
return (ISubject)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------- 在业务方法调用之前可以进行前置增强 ------------");
//利用反射机制调用方法,invoke为返回值,如果没有返回null
Object invoke = method.invoke(target, args);
System.out.println("---------- 在业务方法调用之前可以进行后置增强 ------------");
return invoke;
}
}
二、AOP
2.1基本概念
连接点 (Joinpoint)
程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target)
被通知(被代理)的对象,如上例中的Subjectlmpl
通知(Advice)
在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理),可以理解为AOP真正要实现的功能0代理(Proxy)
将通知应用到目标对象后创建的对象(代理=目标+通知),请注意:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的,如上例中的JDKProxy
切入点(Pointcut)
多个连接点的集合,定义了通知应该应用到那些连接点。也将Pointcut理解成一个条件,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序
适配器(Advisor)
X30 适配器=通知(Advice)+切入点(Pointcut)
AOP远行原理:目标对象只负责逻辑业务编辑,通知只负责AOP增强逻辑(如日志,数据验证等),而代理对象则将业务逻辑而AOP增强代码组织起来(组织者)
2.2AOP的好处
如果每一个业务我们都需要一段公共代码的话,把公共代码放到切面中去,通过动态代理的方式让它发挥作用,这样每个业务功能更加简单,这就是它的作用
通过使用AOP我们可以将日志记录,数据合法性验证,异常处理等功能放入AOP中,那么在业务编写业务的时候就可以专心实现真正的业务逻辑代码。
三、Spring AOP
1.在Spring中org.springframework.aop.framework.ProxyFactorBean用来创建代理对象,在一般情况下它需要注入以下三个属性:
proxylnterfaces 代理应该实现的接口列表(List)
interceptorNames 需要应用到目标对象的通知Bean的名字
target 目标对象(object)
准备工作:创建一个IBookService接口及其实现类,用于演示Spring AOP开发示例:
public interface IBookService {
// 购书
public boolean buy(String userName, String bookName, Double price);
// 发表书评
public void comment(String userName, String comments);
}
创建一个实现类:
public class BookServiceImpl implements IBookService {
public boolean buy(String userName, String bookName, Double price) {
//logger.info("userName={},bookName={},price={}", userName, bookName, price);
// 通过控制台的输出方式模拟购书
System.out.println(userName + " buy " + bookName + ", spend " + price);
return true;
}
public void comment(String userName, String comments) {
System.out.println(userName + " say:" + comments);
}
}
配置到Spring-a.xml中
<bean id="BookServiceImpl"
class="com.zking.springdemo.service.BookServiceImpl"></bean>
3.1前置通知
1、前置通知需要实现org.springframework.aop.MethodBeforeAdvice,前置通知将在目标对象调用前调用。示例实现购书系统AOP方式实现日志,简单打印调用的方法及参数
①首先实现一个前置通知类,实现接口MethodBeforeAdvice,并实现接口中的before方法
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
String s = "[前置通知]: "
+ this.getClass() + "."
+ method.getName()
+ "将被调用,参数为:"
+ Arrays.toString(args);
System.out.println(s);
}
}
配置到Spring-a.xml中
<bean id="MyMethodBeforeAdvice"
class="com.zking.springdemo.advice.MyMethodBeforeAdvice"></bean>
现在需要解决如何目标联系起来,需要一个组织者-代理
配置
<bean id="bookService" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 配置代理目标 -->
<property name="target" ref="bookServiceTarget"/>
<!-- 配置拦截器列表,拦截器就是通知 -->
<property name="interceptorNames">
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturnAdvice</value>
</list>
</property>
<!-- 代理要实现的接口,代理类与被代理类需要实现相同接口 -->
<property name="proxyInterfaces">
<list>
<value>com.zking.springdemo.service.IBookService</value>
</list>
</property>
</bean>
配置好后写一个测试类
public class Test {
public static void main(String[] args) {
//生成spring的上下文
ApplicationContext cxt = new ClassPathXmlApplicationContext("/spring.xml");
IBookService bookService = SpringBeanUtil.getBean("bookService");
bookService.buy("zs", "hlm", 10D);
}
}
测试便可以了。
总结:AOP相当于一个切面可以很好的节省代码空间,公共代码可以放入AOP中节省代码量