1:动态代理
1.1什么是动态代理
系统启动的时候我告知系统我需要对这样一个类中某个方法执行代理,但是本身是没有创建代理的,系统在启动的时候通过我告诉它的这个条件自动生成一个代理
1.2动态代理的优势
动态代理类:在程序运行时,通过反射机制动态生成。
动态代理类通常代理接口下的所有类。
动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。
动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
1.3基于JDK动态代理实现
InvocationHandler:代用处理器
是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序
对代理实例调用方法时,将对方法进行编码并将其指派到它的调用处理程序的invoke方法。
Proxy类:动态生成字节码(只能是接口)。
Java动态代理只能代理接口
动态代理的示例:
定义一个接口
package com.zking.springdemo.proxydemo;
public interface ISubject {
void hello(String param);
}
实现类,实现这个接口。
package com.zking.springdemo.proxydemo;
public class SubjectImpl implements ISubject{
@Override
public void hello(String param) {
// TODO Auto-generated method stub
}
}
在键一个类实现InvocationHandler
package com.zking.springdemo.proxydemo;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
public class JDKProxy implements InvocationHandler{
//目标对象
private Object target;
public JDKProxy(Object target) {
this.target = target;
}
//通过 target.getClass().getClassLoader()方法时哪个类加载器加载的,需要使用相同的类加载器加载
//getClass()类加载器:把class文件加载到内存中去
public Object newProxy() {
return (ISubject)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
//获取到目标类继承的接口将this对象传入,通过这种方式动态的生成了一个代理
target.getClass().getInterfaces(),
this);
}
//调用到代理上对应的方法会直接给到invoke().
@Override
public Object invoke(Object arg0, Method method, Object[] args) throws Throwable {
System.out.println("---------- 在业务方法调用之前可以进行前置增强 ------------");
//利用反射机制调用方法,invoke为返回值,如果没有返回null
Object invoke = method.invoke(target, args);//调用目标对象上的方法
System.out.println("---------- 在业务方法调用之前可以进行后置增强 ------------");
return invoke;
}
}
获取配置的Bean
package com.zking.springdemo.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpringBeanUtil implements ApplicationContextAware{
private static ApplicationContext cxt;
//作用:通过这个接口可以获取到容器对应的ApplicationContext
//ApplicationContext== 上下文容器
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
cxt =applicationContext;
}
/**
* 根据Bean的id来获取Bean对象
* @param id 配置文件中的bean的id属性
* @return Object
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String id) {
return (T)cxt.getBean(id);
}
//通过SpringBeanUtil方法去点 getBean 传入id 通过保存好的上下文直接返回
}
spring-a,xml文件
<bean class="com.zking.springdemo.util.SpringBeanUtil"/>
注意:要记得改权限定名。
2:AOP
2.1基本概练。
1: 连接点(Joinpoint)
程序执行过程中明确的点,如方法的调用,或者异常的抛出
2:目标(Target)
被通知(被代理的对象),如上列中Subjectlmpl
3:通知(Advice)
特定连接点上执行的动作,同时advice是程序代码的具体实现例如一个实现日志的代码
(aop真正实现的通知)
2.2Aop的好处
如果每一个业务功能通用的代码我们可以把它放到切面中去
3.Spring Aop
加载spring中org.springfromework.aop.framework.ProxyFactoryBean
用来创建代理对象,在一般情况下它需要注入一下三个属性。
proxyInterfaces 代理应该实现接口列表(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);
}
对IBookService类动态生成代理(AOP)
package com.zking.springdemo.service;
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);
}
}
3.1前置通知
前置通知需要实现org.spring.framework.aop.MethodBeforeAdvice.前置通知将在目标对象调用前调用。示例实现购书系统Aop方式实现日志,简单打印调用的方法及参数
1)首先实现一个前置通知类,实现接口MethodBeforeAdvice,并实现接口中的before方法
package com.zking.springdemo.advice;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
String s = "[前置通知]: "
+ this.getClass() + "."
+ method.getName()
+ "将被调用,参数为:"
+ Arrays.toString(args);
System.out.println(s);
}
}
2)将实现的前置通知配置到Spring.xml, 以便于spring管理。根据自己的实际情况聘配置
<bean id="myMethodBeforeAdvice" class="com.zking.springdemo.advice.MyMethodBeforeAdvice"/>
<bean id="myAfterReturnAdvice" class="com.zking.springdemo.advice.MyAfterReturnAdvice"/>
3)需要解决如何将通知和目标联系起来,需要一个组织者
<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>
解释:由这个org.springframework.aop.framework.ProxyFactoryBean类,会自动生成一个代理对象
然后告诉代理,代理的目标
<!-- 配置代理目标 -->
<property name="target" ref="bookServiceTarget"/>
在调用目标方法之前给它一个通知myMethodBeforeAdvice。然后实现它的方法。
在告诉它实现类实现IBookService接口,得到接口通过jdk的动态代理就可以动态生成这个类
后置通知
在连接正常完成执行通知。定义的后置通知类需要实现
package com.zking.springdemo.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterReturnAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] arges, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("[后置通知]: 返利10元");
}
}
Demo类测试
package com.zking.springdemo.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterReturnAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] arges, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("[后置通知]: 返利10元");
}
}
运行结果