代理模式目的:
代理就是对目标方法(例如业务的处理)进行增强(例如增删改中事务的开启与提交),让专业的人干专业的事情。实现解耦过程,让业务层只关心业务,而无需考虑例如事务的开启与提交,日志管理等等这些增强的动作我都不关注了,在业务类里只写业务!最后在代理类里把增强的动作和目标动作结合起来就ok了。
静态代理缺点
静态代理是程序运行前,代理类的.class文件已经存在了。
静态代理的缺点:
1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要创建100个代理类
2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码
3、由第一点和第二点可以得出:静态代理的重用性不强
动态代理实现的目的和静态代理一样,都是对目标方法进行增强,而且让增强的动作和目标动作分开,达到解耦的目的
例子:
1、方法的接口
package com.cj.study.proxy;
public interface PersonService {
public void savePerson();
public void updatePerson();
public void deletePerson();
}
2、目标实现类(只关注业务)
package com.cj.study.proxy;
public class PersonServiceImpl implements PersonService{
@Override
public void savePerson() {
System.out.println("添加");
}
@Override
public void updatePerson() {
System.out.println("修改");
}
@Override
public void deletePerson() {
System.out.println("删除");
}
}
3、增强类(例如开启事务提交事务与业务无关等方法封装在这个类中)
package com.cj.study.proxy;
public class Transaction {
public void beginTransaction(){
System.out.println("开启事务 ");
}
public void commit(){
System.out.println("提交事务");
}
}
4、写一个代理类,与目标类实现同样的接口
需要目标类与增强类的对象(本例子也可以使用以下是用构造器方法来注入对象)
以下方式是配置xml文件利用<bean>标签(作用用于配置对象让spring来创建的
)的方式注入对象,重写同样的方法,调用增强类对象与目标类对象中的方法就
OK啦
package com.cj.study.proxy;
public class PersonServiceProxy implements PersonService{
//目标类
private PersonService personService;
//增强类
private Transaction transaction;
@Override
public void savePerson() {
transaction.beginTransaction();
personService.savePerson();
transaction.commit();
}
@Override
public void updatePerson() {
transaction.beginTransaction();
personService.updatePerson();
transaction.commit();
}
@Override
public void deletePerson() {
transaction.beginTransaction();
personService.deletePerson();
transaction.commit();
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="personServie" class="com.cj.study.proxy.PersonServiceImpl"></bean>
<bean id="transaction" class="com.cj.study.proxy.Transaction"></bean>
<bean id="personServiceProxy" class="com.cj.study.proxy.PersonServiceProxy">
<constructor-arg index="0" ref="personServie"></constructor-arg>
<constructor-arg index="1" ref="transaction"></constructor-arg>
</bean>
</beans>
测试:
package com.cj.study.proxy;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ProxyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("com/cj/study/proxy/applicationContext-proxy.xml");
PersonService personService = (PersonService)context.getBean("personServiceProxy");
personService.savePerson();
}
}
动态代理
动态代理是,在程序运行时运用反射机制动态创建而成,无需手动编写代码
动态代理类别
动态代理分为JDK的动态代理和cglib动态代理
它俩有略微的差别:JDK动态代理产生的代理类和目标类实现了相同的接口;cglib动态代理产生的代理类是目标对象的子类
JDK的动态代理
1、同样的接口
package com.cj.study.proxyjdk;
public interface PersonService {
public String savePerson();
public void updatePerson();
public void deletePerson();
}
2、同样的目标类
package com.cj.study.proxyjdk;
public class PersonServiceImpl implements PersonService{
@Override
public String savePerson() {
System.out.println("添加");
return "保存成功!";
}
@Override
public void updatePerson() {
System.out.println("修改");
}
@Override
public void deletePerson() {
System.out.println("删除");
}
}
3、同样的增强类
package com.cj.study.proxyjdk;
public class MyTransaction {
public void beginTransaction(){
System.out.println("开启事务 ");
}
public void commit(){
System.out.println("提交事务");
}
}
4、首先我们需要只要在JDK的API中有一个名为Proxy的类,其中有一个名为
Object newProxyInstance
(ClassLoader loader,Class<?>[] interfaces,InvocationHandle h)
的方法
loader参数是目标类的类加载器,通过反射获得
interfaces参数是目标类实现的接口,通过反射获得,因为可能有多个,所以是一个数组
h参数是一个拦截器的接口,这个拦截器需要我们自己去实现,利用多态的思想传入拦截器对象
5、编写拦截器,实现newProxyInstance方法中的InvocationHandle接口
用的构造器方法注入目标类与增强类的对象
package com.cj.study.proxyjdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PersonServiceInterceptor implements InvocationHandler{
//目标类
private Object target;
//增强类
private MyTransaction myTransaction;
//构造函数注入目标类和增强类
public PersonServiceInterceptor(Object target,MyTransaction myTransaction){
this.target = target;
this.myTransaction = myTransaction;
}
//代理类的每一个方法被调用的时候都会调用下边的这个invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
this.myTransaction.beginTransaction();
Object returnValue = method.invoke(this.target, args);
this.myTransaction.commit();
return returnValue;
}
}
6、我们发现InvocationHandle这个接口中有一个invoke的方法
public Object invoke(Object proxy, Method method, Object[] args)
这个方法在newProxyInstance返回代理对象,代理对象调用方法时,都会调用下面的invoke()方法
所以在invoke()方法内要调用好增强类方法中该做的事情,与利用反射机制动态的调用目标类的方法。
它的三个参数分别是
invoke三个参数:
proxy:就是代理对象,newProxyInstance方法的返回对象,proxy参数传递的即是代理类的实例
method:调用的方法,你在测试中利用返回的代理类调用的是哪个方法,他就是哪个方法的method(就是反射里的那个Method类)
args: 方法中的参数 就是调用方法里的参数
1、当客户端执行代理对象.方法时,进入到了拦截器的invoke方法体
2、拦截器中invoke方法体的内容就是代理对象方法体的内容
3、拦截器中invoke方法的method参数是在调用的时候赋值的
7、我们会发现在拦截器的invoke()方法当中还有一个method.invoke()方法,这个方法是反射里面的,里面有两个参数
一是传入的对象,二是方法的实参。
具体可以跳到另一篇博客去简单的理解反射
8、测试
package com.cj.study.proxyjdk;
import java.lang.reflect.Proxy;
import org.junit.Test;
public class ProxyTest {
@Test
public void test(){
Object target = new PersonServiceImpl();
MyTransaction myTransaction = new MyTransaction();
PersonServiceInterceptor interceptor = new PersonServiceInterceptor(target, myTransaction);
PersonService personService = (PersonService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),interceptor);
String returnValue = (String)personService.savePerson();
System.out.println(returnValue);
}
}
就实现了利用JDK动态代理AOP面向切面编程。
那怎么实现只针对某个接口里的某个方法拦截,而不是针对接口里所有方法都拦截呢?
只需要在调用invoke方法里,method调用前,加个if判断嘛,根据method,getName().equal(“具体方法”)