※ 代理模式
代理模式的特征是代理类与委托类有同样的接口(一般情况下)
代理类主要负责给委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联
代理类的对象本身并不真正实现功能,而是通过调用委托类的对象的相关方法,来提供特定的功能
注意:
委托类对象就是我们后面说到的"目标对象", 也就是需要【被】代理的对象 target
代理类对象就是我们后面说到的"代理对象",目标对象就是需要这个对象为其做为代理 proxy
按照代理类的创建时期,代理类可分为两种。
静态代理类:
在程序运行前,代理类的.class文件就已经存在了
动态代理类:
在程序运行时,代理类是运用了反射技术或字节码技术动态创建而成的
※ 静态代理
例如:
接口: HelloService
委托类: HelloServiceImpl
代理类: HelloServiceProxy
public interface HelloService{
public String sayHello(String msg);
}
public class HelloServiceImpl implements HelloService{
public String sayHello(String msg){
return "hi!"+msg;
}
}
public class HelloServiceProxy implements HelloService{
//目标对象
private HelloService target;
//构造器中接收目标对象
public HelloServiceProxy(HelloService target){
this.target=target;
}
public String sayHello(String msg){
System.out.println("目标方法调用前");
//调用目标对象的方法
//这个方法才是我们真正要执行的方法
String result = target.sayHello(msg);
System.out.println("目标方法调用后");
return result;
}
}
main:
HelloService target =
new HelloServiceImpl();
HelloService proxy =
new HelloServiceProxy(target);
System.out.println(proxy.sayHello("hello"));
测试例:
1.构建接口BookService实现类BookService
package com.briup.pojo;
public interface BookService {
void saveBook(long id,String name);
String get(long id);
void list();
void setTest();
}
package com.briup.pojo;
public class BookServiceImpl
implements BookService{
@Override
public void saveBook(long id, String name) {
// TODO Auto-generated method stub
System.out.println("saveBook....");
}
@Override
public String get(long id) {
// TODO Auto-generated method stub
System.out.println("get....");
return "test...ok";
}
@Override
public void list() {
// TODO Auto-generated method stub
System.out.println("list....");
}
@Override
public void setTest() {
// TODO Auto-generated method stub
System.out.println("set.....");
}
}
2.静态代理类:
package com.briup.aop.proxy.staticProxy;
import com.briup.pojo.BookService;
import com.briup.pojo.BookServiceImpl;
public class BookServiceproxy
implements BookService{
private BookServiceImpl bs;
@Override
public void saveBook(long id, String name) {
// TODO Auto-generated method stub
System.out.println("before....");
bs.saveBook(id, name);
System.out.println("after....");
}
@Override
public String get(long id) {
// TODO Auto-generated method stub
System.out.println("before....");
String str=bs.get(id);
System.out.println("after....");
return str;
}
@Override
public void list() {
// TODO Auto-generated method stub
System.out.println("before....");
bs.list();
System.out.println("after....");
}
public BookServiceImpl getBs() {
return bs;
}
public void setBs(BookServiceImpl bs) {
this.bs = bs;
}
@Override
public void setTest() {
// TODO Auto-generated method stub
}
}
3.配置文件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.xsd"
>
<bean name="target"
class="com.briup.pojo.BookServiceImpl">
</bean>
<bean name="service"
class="com.briup.aop.proxy.staticProxy.BookServiceproxy">
<property name="bs" ref="target"></property>
</bean>
</beans>
4.测试类:
package com.briup.aop.proxy.staticProxy;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.briup.pojo.BookService;
public class StaticProxyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext cp=
new ClassPathXmlApplicationContext(
"com/briup/aop/proxy/staticProxy/staticProxy.xml");
BookService bs=
(BookService) cp.getBean("service");
bs.saveBook(1, "tom");
bs.get(1);
bs.list();
}
}
※ 动态代理
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为使用它可以生成任意类型的动态代理类
java.lang.reflect包下面的
Proxy类和InvocationHandler接口
提供了生成动态代理类的能力
例子:
接口:
public interface IStudentService {
void save(Student s);
void delete(long id);
Student find(long id);
}
实现类
public class StudentServiceImpl implements IStudentService {
public void delete(long id) {
// 记录日志
System.out.println("student is deleted...");
}
public Student find(long id) {
// 记录日志
System.out.println("student is found...");
return null;
}
public void save(Student s) {
// 记录日志
System.out.println("student is saved...");
}
}
日志类:
public class StudentLogger {
public void log(String msg){
System.out.println("log: "+msg);
}
}
//InvocationHandler接口的实现类,java的动态代理中需要使用
public class MyHandler implements InvocationHandler {
//目标对象
private Object target;
private StudentLogger logger = new StudentLogger();
public MyHandler() {
}
public MyHandler(Object target) {
this.target = target;
}
// 参数1 将来所产生的代理对象 Proxy4$
// 参数2 将来需要调用到的目标对象里面真正的那个方法的镜像
// 参数3 将来调用方法的时候所传的参数
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
// 获得将来所调用方法的名字
String methodName = m.getName();
// 用日志记录输出一下
logger.log(methodName + " is invoked...");
// 用反射的方式去调用将来需要真正调用的方法.
Object o = m.invoke(target, args);
return o;
}
get/set
....
}
main:
//目标对象
IStudentService service = new StudentServiceImpl();
//service是我们的目标对象
//我们要给目标对象产生代理对象
//目标对象service只能单独执行delete方法。
//但是我们需要的是:先执行log日志方法再执行delete方法
//目标对象service做不到这个要求,所以我们要给目标对象service生成一个代理对象去完成这俩个操作
/*JDK动态代理的方式获得代理对象*/
//获得目标对象的Class对象
Class c = service.getClass();
//获得目标对象的类加载器对象
ClassLoader classLoader = c.getClassLoader();
//获得目标对象所实现的所有接口
Class[] interfaces = c.getInterfaces();
//获得一个InvocationHandler接口的实现类对象,并把目标对象传进去
InvocationHandler h =
new MyHandler(service);
//参数1 目标对象的类加载器对象
//参数2 目标对象所实现的所有接口. Class类型数组
//参数3 InvocationHandler接口的实现类对象
IStudentService proxy =
(IStudentService)Proxy.newProxyInstance
(classLoader, interfaces, h);
//这里的proxy是一个实现了IStudentService接口动态生成的代理类的对象
proxy.delete();
JDK动态代理方式:
1.BookService接口和BookServiceImpl实现类同上
package com.briup.pojo;
public interface BookService {
void saveBook(long id,String name);
String get(long id);
void list();
void setTest();
}
package com.briup.pojo;
public class BookServiceImpl
implements BookService{
@Override
public void saveBook(long id, String name) {
// TODO Auto-generated method stub
System.out.println("saveBook....");
}
@Override
public String get(long id) {
// TODO Auto-generated method stub
System.out.println("get....");
return "test...ok";
}
@Override
public void list() {
// TODO Auto-generated method stub
System.out.println("list....");
}
@Override
public void setTest() {
// TODO Auto-generated method stub
System.out.println("set.....");
}
}
2.先写测试类,目前先不将代理类构建对象写在xml文件内,以便分析(注释部分)
package com.briup.aop.proxy.jdkproxy;
import java.lang.reflect.Proxy;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.briup.pojo.BookService;
import com.briup.pojo.BookServiceImpl;
public class JdkProxyTest {
public static void main(String[] args) {
//目标对象的构建
// BookService bs=
// new BookServiceImpl();
// //处理器的构建
// MyHandler handler=
// new MyHandler();
// //给处理器设置目标对象
// handler.setTarget(bs);
/*
*Proxy类 jdk提供的代理类
* 前提:目标对象必须实现接口
* 第一个参数表示类加载器
* 第二个参数目标对象的所有接口
* Class[] interfaces
* 第三个参数处理器器(对切面
* 所包含的所有方法生效)
*/
// BookService proxy=
// (BookService) Proxy.newProxyInstance(
// bs.getClass().getClassLoader()
// , bs.getClass().getInterfaces(),
// handler);
// //proxy.saveBook(1, "tom");
// String name=proxy.get(1);
// System.out.println(name+"---");
// proxy.list();
ClassPathXmlApplicationContext cp=
new ClassPathXmlApplicationContext(
"com/briup/aop/proxy/jdkproxy/jdkProxy.xml");
BookService bs=
(BookService) cp.getBean("proxy");
//bs.saveBook(1, "tom");
bs.setTest();
}
}
3.构建代理类对象中的handler
package com.briup.aop.proxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import com.briup.pojo.BookService;
import com.briup.pojo.BookServiceImpl;
/*
* 方法的处理器(针对整个切面)
*/
public class MyHandler
implements InvocationHandler{
//目标对象
private Object target;
/*
* 第一个参数代理对象
* 第二参数方法目标对象的镜像
* 第三参数表示目标对象中方法的参数
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println(method.getName()+"&&");
// System.out.println(Arrays.toString(args)+"&&");
System.out.println("before....");
//调用该镜像所对应的方法
//第一个参数目标对象的实列
Object obj=method.invoke(target, args);
// System.out.println("return :"+obj);
System.out.println("after....");
return obj;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
4.由于xml文件构建对象不能通过java已给类方法构建bean标签
所以利用spring工厂构建工厂类
package com.briup.aop.proxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import org.springframework.beans.factory.FactoryBean;
public class jdkFactory
implements FactoryBean<Object>{
private Object target;//目标对象
private Class[] interfaces;//目标对象实现的所有接口
private InvocationHandler handler;//处理器
@Override
public Object getObject() throws Exception {
Object proxy=
Proxy
.newProxyInstance(target.getClass().getClassLoader()
, interfaces, handler);
return proxy;
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return null;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Class[] getInterfaces() {
return interfaces;
}
public void setInterfaces(Class[] interfaces) {
this.interfaces = interfaces;
}
public InvocationHandler getHandler() {
return handler;
}
public void setHandler(InvocationHandler handler) {
this.handler = handler;
}
}
5.将测试类中代理类对象的生成写在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.xsd"
>
<!-- 目标对象的配置 -->
<bean name="target"
class="com.briup.pojo.BookServiceImpl"></bean>
<!-- 配置处理器 -->
<bean name="handler"
class="com.briup.aop.proxy.jdkproxy.MyHandler">
<property name="target" ref="target"></property>
</bean>
<bean name="proxy"
class="com.briup.aop.proxy.jdkproxy.jdkFactory">
<property name="target" ref="target"></property>
<property name="interfaces">
<array>
<value>com.briup.pojo.BookService</value>
</array>
</property>
<property name="handler" ref="handler"></property>
</bean>
</beans>
※ CGLib代理
JDK动态代理要求目标类实现接口,才能对其进行代理
对于没有实现接口的类,可以使用CGLib进行动态代理
CGLib采用了非常底层的字节码技术(依靠ASM的jar包,开一种开源的java字节码编辑类库),其原理是通过字节码技术为目标类创建一个子类对象,并在子类对象中拦截所有父类方法的调用,然后在方法调用前后调用后都可以加入自己想要执行的代码
注:高版本的spring中,已经在spring-core-xx.jar中,把cglib和asm的核心代码整合了进来
cglib代理的例子:
//目标类,没有实现任何接口
//通过字节码技术创建这个类的子类,实现动态代理
//这时,目标类作为父类,代理对象就是根据目标类动态生成的子类对象
public class SayHello{
public void say(){
System.out.println("hello");
}
}
public class CglibProxy implements MethodInterceptor{
//创建出一个指定父类型的子类对象
public Object getProxy(Class c){
Enhancer enhancer = new Enhancer();
//设置谁是父类
enhancer.setSuperclass(c);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
//intercept方法会拦截所有代理对象中方法的调用
//obj 参数:将来生成的代理对象
//method参数:将来代理对象所调用的方法的镜像
//args 参数:将来代理对象调用方法时所传的参数
//mproxy参数:该参数可以用来调用到父类中的方法
public Object intercept(Object obj, Method method, Object[] args,MethodProxy mproxy) throws Throwable {
System.out.println("目标方法执行之前");
//调用父类中的方法
Object result = mproxy.invokeSuper(obj, args);
System.out.println("目标方法执行之后");
return result;
}
}
main:
CglibProxy proxy = new CglibProxy();
//通过生成子类的方式创建代理类
SayHello s = (SayHello)proxy.getProxy(SayHello.class);
s.say();
输出结果:
目标方法执行之前
hello
目标方法执行之后
例:
cglib一般用于没有接口的实体类
1.实体类
package com.briup.pojo;
public class ProductService {
public void saveProduct(long id,String name) {
System.out.println("saveProduct...");
}
public int getProduct() {
System.out.println("getProduct...");
return 33;
}
}
2.构建代理类
package com.briup.AOP.proxy.cglibproxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/*
* cglib构建代理对象
* 解决了没有接口的目标对象
* 如何在方法之前和之后做操作
* (有接口也可以用)
*/
public class CglibproxyTest implements MethodInterceptor{
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
/*
* 自定义方法构建代理对象
* 方法如果传参数,参数是目标对象的镜像
*/
public Object getProxy() {
Enhancer en=new Enhancer();
//把目标对象设置成父类
en.setSuperclass(target.getClass());
//回调函数:代理调用目标对象方法的时候执行当前类intercept方法
en.setCallback(this);
return en.create();
}
/*
* 当代理对象执行和目标对象相同方法的时候直接进行intercept
* 第一个参数代理对象
* 第二个参数目标对象方法的镜像
* 第三个参数方法的参数
* 第四个参数代理对象方法的镜像
*/
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("before...");
//Object obj=arg3.invoke(arg0, arg2);
Object obj=arg1.invoke(target, arg2);
System.out.println("after...");
return obj;
}
}
3.配置文件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.xsd"
>
<bean name="target" class="com.briup.pojo.ProductService"></bean>
<bean name="factory" class="com.briup.AOP.proxy.cglibproxy.CglibproxyTest">
<property name="target" ref="target"></property>
</bean>
<!--工厂方法调用产生对象 -->
<bean name="proxy" factory-bean="factory" factory-method="getProxy"></bean>
</beans>
4.测试类
package com.briup.AOP.proxy.cglibproxy;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.briup.pojo.ProductService;
public class CglibTest {
public static void main(String[] args) {
// CglibproxyTest cp=new CglibproxyTest();
// ProductService ps=
// (ProductService) cp.getProxy(ProductService.class);
// ps.saveProduct(1, "jake");
// ps.getProduct();
ClassPathXmlApplicationContext cp=
new ClassPathXmlApplicationContext("com/briup/AOP/proxy/cglibproxy/cglib.xml");
ProductService ps=(ProductService) cp.getBean("proxy");
ps.saveProduct(1, "tom");
ps.getProduct();
}
}