Java代理之(jdk静态代理/jdk动态代理/cglib动态代理/aop/aspectj)

一.概念

代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道。如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法。如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱。所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户。而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品。

专业点说:代理模式是对象的结构型模式,代码模式给某一个对象提供代理,并由代理对象控制原对象(目标对象,被代理对象)的引用。简单点说,就是通过一个工厂生成一个类的代理对象,当客户端使用的时候不直接使用目标对象,而是直接使用代理对象。

二.jdk的静态代理

Jdk的静态代理要求,目标对象和代理对象都要实现相同的接口。然后提供给客户端使用。这个代理对客户端是可见的,其结果图如下:

下面给出一个例子:

首先建立1个接口:UserService.java定义如下方法:

 
package com.xie.service;  

public interface UserService {
    public void addUser(String userId,String userName);
    public void delUser(String userId);
    public void modfiyUser(String userId,String userName);
    public String findUser(String userId);
}
<span style="font-family:Arial;font-size:12px;">package com.xie.service;

public interface UserService {
    public void addUser(String userId,String userName);
    public void delUser(String userId);
    public void modfiyUser(String userId,String userName);
    public String findUser(String userId);
}
</span>

然后实现这个接口的目标对象:UserServiceImpl.java

package com.xie.serviceimpl;

import com.xie.service.UserService;

public class UserServiceImpl implements UserService {

    @Override

    public void addUser(String userId, String userName) {

       System.out.println("UserServiceImpl addUser userId->>"+userId);

    }

    @Override

    public void delUser(String userId) {

       System.out.println("UserServiceImpl delUser userId->>"+userId);

    }

    @Override

    public void modfiyUser(String userId, String userName) {

       System.out.println("UserServiceImpl modfiyUser userId->>"+userId);

    }

    @Override

    public String findUser(String userId) {

       System.out.println("UserServiceImpl findUser userId->>"+userId);

       return "张山";

    }

}

为目标对象创建代理对象:UserServiceImplProxy.java代理对象持有目标对象的引用。

package com.xie.serviceproxy;

import com.xie.service.UserService;

public class UserServiceImplProxy implements UserService {

    private UserService userService;

    public UserServiceImplProxy(UserService userService){

       this.userService = userService;

    }

    @Override

    public void addUser(String userId, String userName) {

       try {

           System.out.println("开始执行:addUser");

           userService.addUser(userId, userName);

           System.out.println("addUser执行成功。");

       } catch (Exception e) {

           System.out.println("addUser执行失败。");

       }

    }

    @Override

    public void delUser(String userId) {

    }

    @Override

    public void modfiyUser(String userId, String userName) {

    }

    @Override

    public String findUser(String userId) {

       return null;

    }

}

最后调用代理对象完成功能:Client.java

package com.xie.client;

import com.xie.service.UserService;

import com.xie.serviceimpl.UserServiceImpl;

import com.xie.serviceproxy.UserServiceImplProxy;

public class Client {

         public static void main(String[] args) {

          UserService userService = new UserServiceImplProxy(new UserServiceImpl());

          userService.addUser("001", "centre");

         }

}

三.jdk动态代理

静态代理要为每个目标类创建一个代理类,当需要代理的对象太多,那么代理类也变得很多。同时代理类违背了可重复代理只写一次的原则。jdk给我们提供了动态代理。其原理图如下:

Jdk的动态要求目标对象必须实现接口,因为它创建代理对象的时候是根据接口创建的。如果不实现接口,jdk无法给目标对象创建代理对象。被代理对象可以可以实现多个接口,创建代理时指定创建某个接口的代理对象就可以调用该接口定义的方法了。

首先定义2个接口:Service接口和UserService接口(上面的接口)

Service.java

package com.xie.service;

public interface Service {

      public void sayHello(String name);

      public int addOperter(int num,int num2);

}

然后,定义实现这2个接口的目标对象:UserServiceImpl.java

package com.xie.serviceimpl;

import com.xie.service.Service;

import com.xie.service.UserService;

public class UserServiceImpl implements UserService ,Service{

         @Override

         public void addUser(String userId, String userName) {

                   System.out.println("UserServiceImpl addUser userId->>"+userId);

         }

         @Override

         public void delUser(String userId) {

                   System.out.println("UserServiceImpl delUser userId->>"+userId);

         }

         @Override

         public void modfiyUser(String userId, String userName) {

                   System.out.println("UserServiceImpl modfiyUser userId->>"+userId);

         }

         @Override

         public String findUser(String userId) {

                   System.out.println("UserServiceImpl findUser userId->>"+userId);

                   return "张山";

         }

         @Override

         public void sayHello(String name) {

                   System.out.println("你好:"+name);

         }

         @Override

         public int addOperter(int num, int num2) {

                   return num+num2;

         }

}

提供一个生成代理对象的的类:LogHandler.java

package com.xie.serviceproxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class LogHandler implements InvocationHandler {

         private Object targertObject;

         public Object newInstance(Object targertObject){

                   this.targertObject = targertObject;

                   Class targertClass = targertObject.getClass();

                   return Proxy.newProxyInstance(targertClass.getClassLoader(), targertClass.getInterfaces(),this);

         }

         @Override

         public Object invoke(Object proxy, Method method, Object[] args)

                            throws Throwable {

                   System.out.println("调用方法"+method.getName());

                   Object ret = null;

                   try {

                            ret = method.invoke(targertObject, args);

                            System.out.print("成功调用方法:"+method.getName()+";参数为:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.println(args[i]);

                            }

                   } catch (Exception e) {

                            e.printStackTrace();

                            System.out.print("调用方法:"+method.getName()+"失败;参数为:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.print(args[i]);

                            }

                   }

                   return ret;

         }

}

编写一个客户端类来使用工厂类生成目标类的代理:Client.java

package com.xie.client;

import com.xie.service.Service;

import com.xie.service.UserService;

import com.xie.serviceimpl.UserServiceImpl;

import com.xie.serviceproxy.LogHandler;

public class Client {

         public static void main(String[] args) {

                   Service Service = (Service)new LogHandler().newInstance(new UserServiceImpl());

                   UserService userService = (UserService)new LogHandler().newInstance(new UserServiceImpl());

                   userService.addUser("001", "centre");

                   String name = userService.findUser("002");

                   System.out.println(name);

                   int num = Service.addOperter(12, 23);

                   System.out.println(num);

                   Service.sayHello("centre");

         }

}

四.cglib 动态代理

jdk给目标类提供动态要求目标类必须实现接口,当一个目标类不实现接口时,jdk是无法为其提供动态代理的。cglib 却能给这样的类提供动态代理。Spring在给某个类提供动态代理时会自动在jdk动态代理和cglib动态代理中动态的选择。

使用cglib为目标类提供动态代理:需要导入cglib.jar和asm.jar。如果出现asm中的类无法找到的异常,在java工程中是真的缺少asm.jar,而在web工程中很可能是asm.jar和spring提供的org.springframework.asm-3.0.4.RELEASE.jar包冲突。

首先编写一个目标类:UserServiceImpl.java(上面的类),然后,为其创建一个代理工厂,用于生成目标类的代理对象:CglibProxy.java。

注意:如果一个类继承了某个类,在子类中没有一个方法,用cglib生成该子类的动态代理类中将没有一个方法。

package com.xie.serviceproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor{

         @Override

         public Object intercept(Object obj, Method method, Object[] args,

                            MethodProxy proxy) throws Throwable {

        System.out.println("调用的方法是:" + method.getName());

        Object ret = null;

        try {

            ret = proxy.invokeSuper(obj, args);

                            System.out.print("成功调用方法:"+method.getName()+";参数为:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.print(args[i]);

                            }

                   } catch (Exception e) {

                            e.printStackTrace();

                            System.out.print("调用方法:"+method.getName()+"失败;参数为:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.print(args[i]);

                            }

                   }

                   return ret;

         }

}

编写一个客户端使用代理工厂生成代理对象:CglibClient.java

package com.xie.client;

import net.sf.cglib.proxy.Enhancer;

import com.xie.serviceimpl.UserServiceImpl;

import com.xie.serviceproxy.CglibProxy;

public class CglibClient {

         public static void main(String[] args) {

                   cglibUse1();

         }

         public static void cglibUse1(){ 

                   Enhancer enhancer = new Enhancer();

                   // 设置被代理的类(目标类)

                   enhancer.setSuperclass(UserServiceImpl.class);

                   //使用回调

                   enhancer.setCallback(new CglibProxy());

                   // 创造 代理 (动态扩展了UserServiceImpl类)

                   UserServiceImpl my = (UserServiceImpl) enhancer.create();

                   //my.addUser("001", "centre");

                   int ret = my.addOperter(15, 22);

                   System.out.println("返回的结果是:"+ret);

         }

}

五. jdk动态和cglib动态代理比较

Jdk动态代理要求被代理的类要实现接口,而cglib不需要,cglib能根据内存中为其创建子类(代理对象)。那么它们的效率是怎么样的呢?可以做如下测试:

我们用jdk和cglib动态代理分别为同一个代理创建10000个代理对象。

代码1:

    public static void testFlexiable(){

       UserService test = new UserServiceImpl();

       long nums = 1000;

       Date start = new Date();

       for (int i = 0; i < nums; i++) {

           UserService userService = (UserService)new LogHandler().newInstance(test);

       }

       Date end = new Date();

       System.out.println("创建"+nums+"个代理代理对象用时:"+(end.getTime()-start.getTime())+"毫秒。");

    }

测试结果:

创建1000个代理代理对象用时:32毫秒。

创建1000个代理代理对象用时:31毫秒。

创建1000个代理代理对象用时:31毫秒。

创建1000个代理代理对象用时:31毫秒。

创建10000个代理代理对象用时:94毫秒。

创建10000个代理代理对象用时:78毫秒。

创建10000个代理代理对象用时:78毫秒。

创建10000个代理代理对象用时:78毫秒。

代码2:

    public static void testFlexiable(){

       Enhancer enhancer = new Enhancer();

       // 设置被代理的类(目标类)

       enhancer.setSuperclass(UserServiceImpl.class);

       //使用回调

       enhancer.setCallback(new CglibProxy());

       long nums = 1000;

       Date start = new Date();

       for (int i = 0; i < nums; i++) {

           UserServiceImpl my = (UserServiceImpl) enhancer.create();

       }

       Date end = new Date();

       System.out.println("创建"+nums+"个代理代理对象用时:"+(end.getTime()-start.getTime())+"毫秒。");

    }

测试结果:

创建1000个代理代理对象用时:47毫秒。

创建1000个代理代理对象用时:62毫秒。

创建1000个代理代理对象用时:62毫秒。

创建1000个代理代理对象用时:47毫秒。

创建1000个代理代理对象用时:47毫秒。

创建1000个代理代理对象用时:47毫秒。

创建10000个代理对象会抛异常,cglib运行速度明显比jdk动态代理慢,由于是通过类创建的子类,比jdk通过接口创建代理更耗内存。因此在s2sh框架中,spring通过为类提供代理采用jdk比cglib应该要好一些吧。

六. 面向切面编程

面向切面编程是继面向对象后,又一种重要的思维方式。面向对象比较重要的是通过继承实现代码重用。而面向切面编程,则注重纵向编程,他能将2个不同的功能分开,实现最大程度的解耦,比如我们现在有业务逻辑层和日志层,如果不分开,那么在每个业务逻辑方法中除了要实现业务外还要加上日志代码,如果某一天我不需要日志了,而有很多这样的类的,很多方法都加上日志代码,那改动的工作量是可想而知的。有没有一种方法让我们只写一次日志代码,而把他应用在需要写日志的方法前面,当不需要的时候直接删除呢?面向切面编程给我们提供了办法。

Struts2的拦截器就是采用这种思想编写的。下面模拟实现一个拦截器,设计图如下:

首先定义一个拦截器接口:Interceptor.java

package com.xie.interceptor;

public interface Interceptor {

       public void intercept(ActionInvocation invocation);

}

然后实现拦截器接口,实现3个拦截器:

FirstInterceptor.java,SecondInterceptor.java,ThirdInterceptor.java

package com.xie.interceptor;

public class FirstInterceptor implements Interceptor {

    @Override

    public void intercept(ActionInvocation invocation) {

        System.out.println(1);

        invocation.invoke();

        System.out.println(-1);

    }

}

package com.xie.interceptor;

public class SecInterceptor implements Interceptor {

    @Override

    public void intercept(ActionInvocation invocation) {

        System.out.println(2);

        invocation.invoke();

        System.out.println(-2);

    }

}

package com.xie.interceptor;

public class ThirInterceptor implements Interceptor{

    @Override

    public void intercept(ActionInvocation invocation) {

       System.out.println(3);

       invocation.invoke();

       System.out.println(-3);

    }     

}

接下来定义一个ActionInvocation.java

package com.xie.interceptor;

import java.util.ArrayList;

import java.util.List;

public class ActionInvocation {

         List<Interceptor> interceptors=new ArrayList<Interceptor>();

         int index=-1;

         Action action=new Action();

         public ActionInvocation(){

                   this.interceptors.add(new FirstInterceptor());

                   this.interceptors.add(new SecInterceptor());

                   this.interceptors.add(new ThirInterceptor());

         }

    public void invoke(){

             if (index+1>=this.interceptors.size()) {

                            action.execute();

                   }else {

                            index++;

                            this.interceptors.get(index).intercept(this);

                   }

    }

}

定义一个action:Action.java

package com.xie.interceptor;

public class Action {

     public void execute(){

     System.out.println("execute action.");

     }

}

最后定义Main类来调用action的execute方法:

package com.xie.interceptor;

public class Main {

    public static void main(String[] args) {

       new ActionInvocation().invoke();

    }

}

运行结果如下:

1

2

3

execute action.

-3

-2

-1

在javaEE中,像Filter(过滤器),Intercepetor(拦截器),spring的事务管理都采用了面向切面的编程思想。

1.几个应用

写一个过滤器:这个过滤器的功能是实现web页面访问权限:

package com.ibeifeng.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

public class LoginFilter implements Filter {

         public void destroy() {

         }

         public void doFilter(ServletRequest req, ServletResponse res,

                            FilterChain chain) throws IOException, ServletException {

                   HttpServletRequest request = (HttpServletRequest) req;

                   HttpSession session = request.getSession();

                   String username = (String) session.getAttribute("username");

                   HttpServletResponse response = (HttpServletResponse) res;

                   String uri = request.getRequestURI();

                   //如果用户请求了index.html,这时就必须做登录判断,判断用户是否登录。

                   if("/Pfms/index.html".equals(uri)) {

                            if(username == null || "".equals(username)) {

                                     response.sendRedirect("login.html");

                            } else {

                                     chain.doFilter(request, response);

                            }

                   }else {

                            chain.doFilter(request, response);

                   }

         }

         public void init(FilterConfig filterConfig) throws ServletException {

         }

}

再写一个struts2的拦截器:

package interceptor;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.*;

import com.opensymphony.xwork2.*;

import java.util.*;

public class AuthorizationInterceptor extends AbstractInterceptor

{

         private String ignoreActions;

         // ignoreActions属性的getter方法

         public String getIgnoreActios()

         {

                   return ignoreActions;

         }

         // ignoreActions属性的setter方法

         public void setIgnoreActions(String ignoreActions)

         {

                   this.ignoreActions = ignoreActions;

         }

         @Override

         public String intercept(ActionInvocation invocation) throws Exception

         {

                   ActionContext ctx = invocation.getInvocationContext();

                   Map session = ctx.getSession();

                   String user = (String) session.get("username");

                   boolean ignore = false;

                   String currentAction = invocation.getProxy().getActionName();

                   String[] actions = ignoreActions.split(",");

                   for (String action : actions)

                   {

                            if (currentAction.matches(action.trim()))

                            {

                                     ignore = true;

                                     break;

                            }

                   }

                   if (user != null || ignore == true)

                   {

                            return invocation.invoke();

                   }

                   else

                   {

                            return Action.LOGIN;

                   }

         }

}

看看spring是如何配置事物的(xml):

    <!-- 配置事务管理器 -->

    <bean id="txManager"

       class="org.springframework.orm.hibernate3.HibernateTransactionManager">

       <property name="sessionFactory">

           <ref local="sessionFactory" />

       </property>

       <property name="nestedTransactionAllowed" value="true" />

    </bean>

    <!-- 配置事务的传播特性 -->

    <tx:advice id="txAdvice" transaction-manager="txManager">

       <tx:attributes>

           <!-- 在开发的时候可以这样定义,但部署的时候一定要详细定义 -->

           <tx:method name="*" propagation="REQUIRED" />

           <tx:method name="get*" read-only="true" />

           <tx:method name="load*" read-only="true" />

           <tx:method name="find*" read-only="true" />

           <tx:method name="query*" read-only="true" />

           <tx:method name="pagedQuery*" read-only="true" />

           <tx:method name="*" />

       </tx:attributes>

    </tx:advice>

    <!-- 以AspectJ方式 定义 AOP -->

    <aop:config proxy-target-class="true" expose-proxy="true">

       <aop:advisor pointcut="execution(public * com.hikvision.webclient8100.manager.*.*(..))"

           advice-ref="txAdvice" />

    </aop:config>

2.面向切面的概念

1)aspect(切面):实现了cross-cutting功能,是针对切面的模块。最常见的是logging模块,这样,程序按功能被分为好几层,如果按传统的继承的话,商业模型继承日志模块的话根本没有什么意义,而通过创建一个logging切面就可以使用AOP来实现相同的功能了。

将横切多个业务对象的程序对了出来,模块化,该模块可以无侵入式的集成到业务对象中,如:事务,日志,权限等。

2)jointpoint(连接点):连接点是切面插入应用程序的地方,该点能被方法调用,而且也会被抛出意外。连接点是应用程序提供给切面插入的地方,可以添加新的方法。比如以上我们的切点可以认为是findInfo(String)方法。

通知执行的时机,如方法调用,抛出异常时。

3)advice(处理逻辑):advice是我们切面功能的实现,它通知程序新的行为。如在logging里,logging advice包括logging的实现代码,比如像写日志到一个文件中。advice在jointpoint处插入到应用程序中。以上我们在MyHandler.java中实现了advice的功能 。

Advice(通知):指切面的具体实现,如记录日志,验证权限。通知有各种类型,其中包括“before”,“after”,“around”和“throw”等通知

4)pointcut(切入点):pointcut可以控制你把哪些advice应用于jointpoint上去,通常你使用pointcuts通过正则表达式来把明显的名字和模式进行匹配应用。决定了那个jointpoint会获得通知。

切入点:是感兴趣的连接点。通知和一个切入点表达式关联,并在满足这个切入点上运行,(如:执行某个特定名称的方法时。)切入点表达式如何和连接点匹配时AOP的核心:spring缺省使用AspectJ切入点语法。

  5)introduction:允许添加新的方法和属性到类中。

6)target(目标类):是指那些将使用advice的类,一般是指独立的那些商务模型。比如以上的StudentInfoServiceImpl.

是一个被代理对象,被通知对象,被一个或者多个切面所通知的对象。

7)proxy(代理类):使用了proxy的模式。是指应用了advice的对象,看起来和target对象很相似。

AOP代理的对象,用来实现切面的功能,在spring中,AOP代理可以使用jdk动态代理和cglib动态代理。

8)weaving(插入):是指应用aspects到一个target对象创建proxy对象的过程:complie time,classload time,runtime。把切面连接到其它应用程序类型或者对象上,并创建一个被通知的对象,在运行时完成织入。

3.使用aspectj实现aop编程

使用Aspectj编写系统日志。

Aspectj是一个使用面向切面,底层采用动态代理的框架。AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。spring的AOP实现使用了Aspectj框架。

1.       采用annotation

a)首先开启aspectj支持,如下:

<?xml version="1.0" encoding="UTF-8"?>

<beans default-lazy-init="false"

    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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="

           http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

           http://www.springframework.org/schema/context

           http://www.springframework.org/schema/context/spring-context-3.0.xsd

           http://www.springframework.org/schema/tx

           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

           http://www.springframework.org/schema/aop

            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 </beans>

b)定义切面对象

@Aspect

c)申明切入点

任何public方法的执行:

execution(public * *(..))

任何以set开头的方法:

execution(* set*(..))

任何在接口AccountService的方法:

execution(* com.xyz.service.AccountService.*(..))

任何在service包的方法:

execution(* com.xyz.service.*.*(..))

任何在service包和其子包的方法:

execution(* com.xyz.service..*.*(..))

any join point (执行方法必须在Spring Aop方式下) within the service package:

within(com.xyz.service.*)

any join point (执行方法必须在Spring Aop方式下) within the service package or a sub-package:

within(com.xyz.service..*)

any join point (执行方法必须在Spring Aop方式下) where the proxy implements the AccountService interface:

this(com.xyz.service.AccountService)

any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:

target(com.xyz.service.AccountService)

‘target‘ is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.

any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:

args(java.io.Serializable)

‘args‘ is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:

@target(org.springframework.transaction.annotation.Transactional)

‘@target‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:

@within(org.springframework.transaction.annotation.Transactional)

‘@within‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:

@annotation(org.springframework.transaction.annotation.Transactional)

‘@annotation‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified annotation:

@args(com.xyz.security.Classified)

‘@args‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.

any join point (method execution only in Spring AOP) on a Spring bean named ‘tradeService‘:

bean(tradeService)

any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression ‘*Service‘:

bean(*Service)

d)切入点表达式:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?

name-pattern(param-pattern) throws-pattern?)

--modifiers-pattern 访问修饰符,public,private等

-- ret-type-pattern 返回类型,void,String等

-- declaring-type-pattern 申明类型

-- name-pattern 方法名称

-- param-pattern 参数名称

-- throws-pattern 异常名称

编写一个接口:ServiceAspectjAnnotation.java

package com.xie.aspectj.test;

import java.util.List;

import com.hikvision.webclient8100.domain.Depart;

public interface ServiceAspectjAnnotation {

    public boolean delete(List<Depart> list);

    public boolean change(List<Depart> list);

    public List<Depart> queryAll();

    public Depart queryByName(String pname);

    public Depart queryBySequenceNo(String sequenceNo);

    public String pageDivide(int index, int pageSize);

    public String findParentOrgsName() throws Exception;

    boolean save(List<Depart> list);

}

实现该接口:ServiceAspectjAnnotationImpl.java

package com.xie.aspectj.test;

import java.util.List;

import org.springframework.stereotype.Component;

import com.hikvision.webclient8100.domain.Depart;

@Component(value="serviceAspectjAnnotationImpl")

public class ServiceAspectjAnnotationImpl //implements ServiceAspectjAnnotation

{

    public boolean save(List<Depart> list) {

       System.out.println("保存方法执行成功。");

       return false;

    }

    public boolean delete(List<Depart> list) {

       System.out.println("删除方法执行成功。");

       return false;

    }

    public boolean change(List<Depart> list) {

       System.out.println("修改方法执行成功。");

       return false;

    }

    public List<Depart> queryAll() {

       System.out.println("查询成功。");

       return null;

    }

    public Depart queryByName(String pname) {

       System.out.println("通过名字查询方法执行成功。");

       return null;

    }

    public Depart queryBySequenceNo(String sequenceNo) {

       System.out.println("通过序列号查询成功。");

       return null;

    }

    public String pageDivide(int index, int pageSize) {

       System.out.println("分页方法成功返回。");

       return null;

    }

    public String findParentOrgsName() throws Exception {

       System.out.println("查找上级组织名称成功。");

           throw new Exception();

    }

}

建立一个日志类:LogHandler.java

package com.hikvision.webclient8100.util;

import org.apache.log4j.Logger;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.springframework.stereotype.Component;

@Component(value = "logHandler")

@Aspect

// 切面

public class LogHandler {

    private static Logger logger = Logger.getLogger(LogHandler.class);

    @Before// 通知

    // * com.hikvision.webclient8100.manager.*.*(..))切点

    ("execution(* com.hikvision.webclient8100.manager.*.*(..))")

    // 连接点,连接点是切面插入应用程序的地方

    public void manager() {// 通知,面向切面加入的业务逻辑

       logger.info("manager层的某个方法开始执行");

    }

    @After("execution(* com.xie.aspectj.test.*.*(..))")

    public void dao() {

       logger.info("dao层的某个方法执行完成");

    }

    @Around("execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.change(..))")

    public void arund(ProceedingJoinPoint pjp)  throws Throwable {

       logger.info("方法开始执行....................");

       pjp.proceed();

       logger.info("方法执行完成。");

    }

    @AfterThrowing(pointcut = "execution(* com.xie.aspectj.test.*.*(..))", throwing = "ex")

    public void doRecovery() {

       logger.error("出现异常。");

    }

    @AfterReturning("execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.delete(..))")

    public void reOpertion() {

       logger.info("操作完成");

    }

}

编写测试方法:ServiceAspectjAnnotationImplTest.java

package com.xie.aspectj.test;

import java.util.ArrayList;

import java.util.List;

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.hikvision.webclient8100.dao.DepartDao;

import com.hikvision.webclient8100.domain.Depart;

public class ServiceAspectjAnnotationImplTest {

         private static ClassPathXmlApplicationContext atc = new 

ClassPathXmlApplicationContext("applicationContext.xml");

         @BeforeClass

         public static void setUpBeforeClass() throws Exception {

         }

         @AfterClass

         public static void tearDownAfterClass() throws Exception {

         }

         @Test

         public void testSave(){

                   ServiceAspectjAnnotation serviceAspectjAnnotation = (ServiceAspectjAnnotation) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.save(new ArrayList<Depart>()));

         }

         @Test

         public void testSave1(){

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation = 

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.save(new ArrayList<Depart>()));

         }

         @Test

         public void testDelete(){

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation = 

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.delete(new ArrayList<Depart>()));

         }

         @Test

         public void testChange(){

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation = 

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.change(new ArrayList<Depart>()));

         }

         @Test

         public void testFind() throws Exception{

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation = 

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   serviceAspectjAnnotation.findParentOrgsName();

         }

}

2.使用xml:

    <!-- <bean id="logHandler1" class="com.hikvision.webclient8100.util.LogHandler1"></bean> -->

   <!—采用了spring的注解生成bean-->

    <aop:config><!-- spring 的配置 -->

       <aop:aspect ref="logHandler1"><!-- 定义切面 -->

           <aop:pointcut id="manager1"

              expression="execution(* com.hikvision.webclient8100.manager.*.*(..))" /><!-- 定义切点 -->

           <aop:before pointcut-ref="manager1" method="reOpertion" /><!-- 通知执行时机,这是我们要在主业务逻辑出要加入的代码 -->

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="test"

              expression="execution(* com.xie.aspectj.test.*.*(..))" />

           <aop:after pointcut-ref="test" method="dao" />

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="around"

              expression="execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.change(..))" />

           <aop:around pointcut-ref="around" method="arund" />

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="throw"

              expression="execution(* com.xie.aspectj.test.*.*(..))" />

           <aop:after-throwing pointcut-ref="throw" method="doRecovery"

              throwing="ex" />

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="afterreturn"

              expression="execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.delete(..))" />

           <aop:after-returning pointcut-ref="afterreturn"

              method="reOpertion" />

       </aop:aspect>

    </aop:config>

七.总结

面向切面(方面)编程:实际上就是在不修改原来代码的前提下为原来的业务增加新的逻辑代码。它可以方便的添加和删除,让代码维护起来更加方便。Aspectj是一个AOP框架,底层实现采用动态代理。Spring的AOP采用aspectj,动态代理采用jdk和cglib中切换。

时间: 2024-10-22 01:24:14

Java代理之(jdk静态代理/jdk动态代理/cglib动态代理/aop/aspectj)的相关文章

JDK的动态代理为什么必须要使用接口与使用CGLIB动态代理

一.JDK的动态代理为什么必须要使用接口 JDK的代理Proxy必须要使用接口,才可以实现对方法的拦截.为什么呢?先让我们看一个JDK动态代理的示例: 接口类: public interface IPerson { public void sayHi(String nm); } 接口实现类: public class Person  implements IPerson{ public Person(){//构造 } private String name; public Person(Stri

Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)

一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法.如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱.所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户.而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品. 专业点说:代理模式是对象的结构型模式,代码模式给某一个对象提供代理,并由代理对象控制原对象

JDK动态代理和CGLIB动态代理

转载自http://www.itzhai.com/java-dong-tai-dai-li-zhi-jdk-dong-tai-dai-li-he-cglib-dong-tai-dai-li-mian-xiang-qie-mian-bian-cheng-aop-yuan-li.html 静态代理 静态代理相对来说比较简单,无非就是聚合+多态: 参考:设计模式笔记 – Proxy 代理模式 (Design Pattern) 动态代理 我们知道,通过使用代理,可以在被代理的类的方法的前后添加一些处理方

设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网上已经有很多优质的文章,我这里就不会再过多的介绍了,这里推荐几篇优质的文章作为参考: 给女朋友讲解什么是代理模式 轻松学,Java 中的代理模式及动态代理 另外,我的 github 仓库对应目录中也有相关的基础示例代码:https://github.com/eamonzzz/java-advance

java 静态代理 JDK动态代理 Cglib动态代理

下面以一个简单的银行账户为例讲述讲述动态代理. 设计一个银行账户类,包含用户的账户余额,实现查询和更新余额功能 这个系统用了一段时间,有客户要求对账说账户余额给弄错了?因为上面没有存取款记录,最后银行不认账,客户收到了损失.银行为了避免这种现象再次发生,决定对这个系统进行修改,但是因为bankAccount太过复杂,希望在不修改bankAccount的情况下,增加日志功能. 静态代理 使用静态代理解决上面的问题. 银行要求所有模块都需要添加日志功能,这对苦逼的程序员来说真的是一个不小的工作量啊,

[z]Java代理(jdk静态代理、动态代理和cglib动态代理)

一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 6 7 8 9 10 11 12 13 /**  * 业务接口  * @author pc  *  */ public interface UserService {          // 增加一个用户     public void addUser();     // 编辑账户     pub

Java代理(jdk静态代理、动态代理和cglib动态代理)

一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接口 * @author pc * */ public interface UserService { // 增加一个用户 public void addUser(); // 编辑账户 public void editUser(); } 2.业务实现类 /** * 业务实现类 * @author pc

Java进阶之 JDK动态代理与Cglib动态代理

一.动态代理概述: 与静态代理对照(关于静态代理的介绍 可以阅读上一篇:JAVA设计模式之 代理模式[Proxy Pattern]), 动态代理类的字节码是在程序运行时由Java反射机制动态生成. 注意: 1.AspectJ是采用编译时生成AOP代理类,具有更好的性能,但是需要使用特定的编译器进行处理 2.Spring AOP采用运行时生成AOP代理类,无需使用特定编译器进行处理,但是性能相对于AspectJ较差 二.JDK动态代理 [对有实现接口的对象做代理] 1.JDK动态代理中 需要了解的

Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

作者:亦山 推荐:hh375的图书馆 class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class 文件内的信息,生成对应的 Class对象: class字节码文件是根据JVM虚拟机规范中规定的字节码组织规则生成的.具体class文件是怎样组织类信息的,可以参考 此博文:深入理解Java Class文件格式系列.或者