代理
代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理.
代理模式
代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy)。最大特点是代理类和被代理的类必须实现同样的接口,这就失去了灵活性。代理类和真实主题类都应该是事先已经存在的,代理类的接口和所代理方法都已明确指定,如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。动态代理可以让系统能够根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法。这就是动态代理了。
动态代理
Java语言提供了对动态代理的支持:Proxy类
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h)
该方法用于返回一个动态创建的代理类的实例。第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表,第三个参数h表示所指派的调用处理程序类。
InvocationHandler接口
InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。该接口很简单,只声明了一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
对动态代理对象的调用请求都将会被自动转发给InvocationHandler对象的invoke()方法,由invoke()方法来实现对请求的统一处理。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。
示例
下面通过利用注解的方式记录log的例子来说明。代码结构如下:
解释下,这里加Delegate层主要有三方面原因,一是对Server进行分发,二是对Delegate层进行代理,三是方便在Delegate层进行日志、异常处理等处理。
Service
首先,完成业务处理,这里主要构造一些数据。
package com.my.web.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.my.web.bean.Employee;
import com.my.web.bean.ListBean;
import com.my.web.bean.PersonInfo;
import com.my.web.service.EmployeeService;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Override
public Employee getEmployee(int id) {
return createEmployee();
}
private Employee createEmployee() {
Employee e = new Employee("hust", new BigDecimal("50"), false, 5);
PersonInfo info = new PersonInfo();
info.setPhoneNo("zhanglu6");
info.setAge(26);
info.setAddress("tangjia");
e.setInfo(info);
return e;
}
private List<Employee> getAllEmpoyees() {
List<Employee> list = new ArrayList<Employee>();
list.add(createEmployee());
list.add(createEmployee());
list.add(createEmployee());
return list;
}
@Override
public ListBean getAllEmpoyee() {
ListBean beans = new ListBean();
beans.setList(getAllEmpoyees());
return beans;
}
}
Delegate
该层主要对Service进行分发。另外对函数加注解达到记录日志的目的。注意@ActionLog(action = "GetEmployee")
package com.my.web.delegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.my.web.bean.Employee;
import com.my.web.bean.ListBean;
import com.my.web.log.ActionLog;
import com.my.web.service.EmployeeService;
@Component("ws")
public class MyWebServiceImpl implements MyWebService{
//该层主要用于对service请求进行分发。
@Autowired
private EmployeeService emService;
@ActionLog(action = "GetEmployee")
public Employee getEmployee(int id) {
return emService.getEmployee(id);
}
public ListBean getAllEmpoyee() {
return emService.getAllEmpoyee();
}
}
Controller
为了调用简单,这里我添加了Restful,然后通过URL即可访问后台。注意这里没有用注解的方式进行注入,而是通过setter的方式进行注入,主要是为了完成绑定。
/**
*
*/
package com.my.web.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.my.web.bean.Employee;
import com.my.web.delegate.MyProxy;
import com.my.web.delegate.MyWebService;
import com.my.web.delegate.MyWebServiceImpl;
@RequestMapping(value = "/rest", method = RequestMethod.GET)
public class MyController {
private MyWebService ws;
public MyWebService getWs() {
return ws;
}
//为了进行代理的绑定,这里我使用了setter进行注入。
public void setWs(MyWebService ws) {
MyProxy proxy = new MyProxy(ws, MyWebServiceImpl.class);
this.ws = (MyWebService) proxy.bind();
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public Employee show(@PathVariable int id, HttpServletRequest request,
HttpServletResponse response) throws Exception {
return ws.getEmployee(id);
}
@RequestMapping(value = "/user/list", method = RequestMethod.GET)
public com.my.web.bean.ListBean getAll() throws Exception {
return ws.getAllEmpoyee();
}
}
代理类
下面就是我们今天的主角了。首先,通过反射调用我们的业务函数,然后获取业务函数上的注解内容用于记录日志。package com.my.web.delegate;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.my.web.log.ActionLog;
public class MyProxy implements InvocationHandler {
// 目标对象,完成我们的业务
private Object delegate;
// 真正的实现类
private Class realClass;
public MyProxy(Object delegate, Class realClass) {
this.delegate = delegate;
this.realClass = realClass;
}
/**
* 返回一个全新的对象(写法,基本上固定的),进行绑定
*/
public Object bind() {
return Proxy.newProxyInstance(
this.delegate.getClass().getClassLoader(), this.delegate
.getClass().getInterfaces(), this);
}
/**
* 程序会自动调用该方法,在改方法中添加附加操作。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//通过实现类获取实际的方法,因为注解一般加载实现类的方法上。
Method realMethod = realClass.getMethod(method.getName(),
method.getParameterTypes());
Object obj = null;
// 此处可以执行前置的方法
// 通过反射,执行目标方法。
obj = method.invoke(this.delegate, args);
//此处可以执行后置的方法
doActionLog(obj, realMethod, args);
// 返回值给调用者
return obj;
}
private void doActionLog(Object obj, Method method, Object[] args) {
ActionLog a = method.getAnnotation(ActionLog.class);
if (a != null && obj != null) {
System.out.println(a.action());
// TODO log function
}
}
}
注解类
package com.my.web.log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionLog {
public String action() default "";
public String refType() default "";
}
Spring文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
default-lazy-init="true">
<!-- 通过mvc:resources设置静态资源,这样servlet就会处理这些静态资源,而不通过控制器 -->
<!-- 设置不过滤内容,比如:css,jquery,img 等资源文件 -->
<mvc:resources location="/*.html" mapping="/**.html" />
<mvc:resources location="/css/*" mapping="/css/**" />
<mvc:resources location="/js/*" mapping="/js/**" />
<mvc:resources location="/images/*" mapping="/images/**" />
<!-- 添加注解驱动 -->
<mvc:annotation-driven />
<!-- 默认扫描的包路径 -->
<context:component-scan base-package="com.my.web.*" />
<!-- 注册我们的Controller -->
<bean name="MyController" class="com.my.web.controller.MyController">
<property name="ws" ref="ws"></property>
</bean>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="favorParameter" value="false" />
<property name="ignoreAcceptHeader" value="true" />
<property name="defaultContentType" value="application/xml" /><!-- application/xml -->
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
<entry key="html" value="text/html" />
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean id="jsonView"
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<bean id="xmlView"
class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.my.web.bean.Employee</value>
<value>com.my.web.bean.ListBean</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
</list>
</property>
</bean>
</beans>
总结
一个典型的动态代理创建对象过程可分为以下三个步骤:1、实现InvocationHandler接口来创建自己的调用处理器。
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、利用Proxy类中的newInstance方法创建代理类实例,此时需将调用处理器对象作为参数被传入。