Spring 学习笔记 Part06
1. 动态代理概念
动态代理得类的字节码随用随创建,随用随加载,作用是不修改源码的基础上对方法进行增强。分为两类:① 基于接口的动态代理 ② 基于子类的动态代理 。
2. 基于接口的动态代理
使用的类:Proxy (由 JDK 官方提供的)
创建代理对象的要求:被代理类最少实现了一个接口,如果没有则不能使用
使用的方法 :Proxy.newProxyInstance( )
参数:
ClassLoader :类加载器类型。用于加载代理对象的字节码的 ( 被代理的是谁,就写谁的类加载器 )// producer.getClass( ).getClassLoader( )
Class[ ] :字节码数组。用于让代理对象和被代理对象有相同的方法( 被代理的是谁,就写谁的接口集合 ) // producer.getClass( ).getInterface( )
InvocationHandler :用于提供增强的代码 ( 由我们编写如何代理 ) // new InvocationHandler( )
//经销商类
public class Producer implements ProducerInter{
@Override
public void saleProduct(double money) {
System.out.println("卖出商品了,价格是"+money+"元");
}
}
//未增强代理的客户类
public class Client {
public static void main(String[] args) {
final Producer producer; //匿名内部类访问外部变量时,外部变量必须被final修饰
ProducerInter proxyProducer =
(ProducerInter) Proxy.newProxyInstance(Producer.class.getClassLoader(),
Producer.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/* 这里没有写东西时,是没有意义的,即对方法没有增强,与原方法一模一样 */
return method.invoke(producer,args);//传入被代理对象,传入方法的参数
}
});
proxyProducer.saleProduct(10000);//使用代理对象的方法
}
}
invoke( )方法:执行被代理对象的任何接口方法都会经过该方法
参数:
proxy:代理对象的引用(用不上)
method:当前执行的方法
args:执行方法所需的参数
返回值:和被代理对象方法具有相同的返回值
增强代理代码段可以总结为 3 个步骤 :
1. 获取方法执行的参数
2. 判断当前执行的方法是不是需要增强的
3. return一个返回值,类型和原方法返回值一样
//增强代理的客户类
public class Client {
public static void main(String[] args) {
final Producer producer; //匿名内部类访问外部变量时,外部变量必须被final修饰
ProducerInter proxyProducer =
(ProducerInter) Proxy.newProxyInstance(Producer.class.getClassLoader(),
Producer.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/* 在这里编写代码来对方法进行增强 */
Object returnValue = null;
Double money = (Double)args[0]; //1.获取方法提供的参数
if("saleProduct".equals(method.getName())){ //2.判断当前方法是不是销售
returnValue = method.invoke(producer,money*0.8);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000);//使用代理对象的方法
}
}
method.invoke( ) 用于反射调用方法
参数:
Object obj:指定调用该方法的对象
Object… args:指定该方法的参数
3. 基于子类的动态代理
当我们的类不实现任何接口时,基于接口的动态代理是不能用的。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
使用基于子类的动态代理,需要导入cglib的坐标
使用的类:Enhancer(由 第三方cglib库 提供的)
创建代理对象的要求:被代理类不能是 final 修饰的最终类(如果是最终类,就没有子类了,也就不能创建代理对象了)
使用的方法 :Enhancer.create( )
参数:
Class :字节码类型。用于指定被代理对象的字节码 ( 被代理的是谁,就写谁的类加载器 )// producer.getClass( )
Callback :用于提供增强的代码 ( 由我们编写如何代理 ) 。但一般我们都会使用它的子接口实现类:MethodInterceptor(方法拦截器)// new MethodInterceptor( )
//不再是接口,而是一个类,但是这个类不能是final修饰的最终类
public class Producer {
public void saleProduct(double money) {
System.out.println("卖出商品了,价格是"+money+"元");
}
}
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
Producer superProducer = (Producer) Enhancer.create(Producer.class, new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增强方法代码
}
});
superProducer.saleProduct(12000);
}
}
intercept( )方法:执行被代理对象的任何接口方法都会经过该方法
参数:
proxy:代理对象的引用(用不上)
method:当前执行的方法
args:执行方法所需的参数
//以上三个参数和基于接口的动态代理中 invoke( ) 方法的参数是一样的
methodProxy:当前执行方法的代理对象(更用不上)
返回值:和被代理对象方法具有相同的返回值
//增强代理代码段同基于接口的代理
Object returnValue = null;
Double money = (Double) objects[0];
if("saleProduct".equals(method.getName())){
returnValue = method.invoke(producer,money*0.8);
}
return returnValue;
1. 获取方法执行的参数
2. 判断当前执行的方法是不是需要增强的
3. return一个返回值,类型和原方法返回值一样
4. 动态代理实现事务控制
建立一个BeanFactory类来生产代理对象。
4.1 注解方式
使用 @Autowired 后,代理对象就不需要加 final 了,因为 Spring 已经为我们做好了这个工作。
@Component("beanFactory")
public class BeanFactory {
@Autowired
private AccountService accountService; //本来这个被代理对象要加final属性的,但是使用@Autowired后,Spring已经为我们做好了这个工作
@Autowired
private TransactionManager transactionManager;
@Bean(name = "proxyService") //需要把代理对象加入IoC容器中
public AccountService getAccountService() {
return (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue=null;
//事务控制
try {
transactionManager.begin();
returnValue = method.invoke(accountService, args); //业务层方法调用
transactionManager.commit();
return returnValue;
} catch (Exception e) {
transactionManager.rollback();
throw new RuntimeException(e);
} finally {
transactionManager.release();
}
}
});
}
}
Service层则不加入事务控制代码,直接和最开始一样各自实现业务即可。
4.2 xml配置方式
xml配置方式注入时,final需要自行添加。
public class BeanFactory{
private AccountService accountService;
public final void setAccountService(AccountService accountService){
this.accountService=accountService;
}
}
xml里的配置和最开始一样。
<bean id="beanFactory" class="com.itheima.factory.BeanFactory">
<property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="com.itheima.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
通过工厂类获取bean对象的回顾。
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"/>
工厂创建时,一行解决的方式必须用静态工厂(即工厂类里使用静态方法),两行解决的可以不用。以上例子是使用一行解决的,所以在工厂类中的 getAccountService( ) 方法必须被 static 关键字修饰。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84499.html