反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
反射相关的类:
java.lang.Class;
java.lang.reflect.Constructor;//构造器
java.lang.reflect.Field;//成员变量
java.lang.reflect.Method;//方法
java.lang.reflect.Modifier;//成员访问策略
使用getDeclaredXXX获取所有变量和方法。使用Method.invoke()方法调用方法。获取方法时需要指定方法的参数类型。
使用反射机制可以打破封装性,导致了java对象的属性不安全。在私有对象和方法上调用setAccessible(true)可以访问私有成员。
方法关键字 | 含义 |
getDeclaredMethods() | 获取所有的方法 |
getReturnType() | 获得方法的返回类型 |
getParameterTypes() | 获得方法的传入参数类型 |
getDeclaredMethod("方法名",参数类型.class,……) | 获得特定的方法 |
构造方法关键字 | 含义 |
getDeclaredConstructors() | 获取所有的构造方法 |
getDeclaredConstructor(参数类型.class,……) | 获取特定的构造方法 |
父类和父接口 | 含义 |
getSuperclass() | 获取某类的父类 |
getInterfaces() | 获取某类实现的接口 |
代理:静态代理与动态代理
静态代理就是代理模式的一种应用。静态代理的构建就是实现了被代理类接口的类,其中保存了一个被代理类的实例。在接口的实现方法中,调用被代理对象对应的方法,同时添加需要的其他操作。示例:
A:先定义一个接口类
java 代码
package ttitfly.proxy;
public interface HelloWorld {
public void print(); // public void say();
}
B: 定义一个该接口的实现类
java 代码
package ttitfly.proxy;
public class HelloWorldImpl implements HelloWorld{
public void print(){
System.out.println("HelloWorld");
} // public void say(){ // System.out.println("Say Hello!"); // }
}
C:定义一个静态代理类
java 代码
package ttitfly.proxy;
public class StaticProxy implements HelloWorld{
public HelloWorld helloWorld ;
public StaticProxy(HelloWorld helloWorld){
this.helloWorld = helloWorld;
}
public void print(){
System.out.println("Welcome");
<span style="color:#cc0000;">//相当于回调</span>
helloWorld.print();
}
// public void say(){ // //相当于回调 // helloWorld.say(); // }
}
java动态代理机制以巧妙的方式实现了代理模式的设计理念。当需要对一个对象添加代理时,可以使用动态代理,如果只有类型对象,并没有实例对象,这时候可以使用静态代理。
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:点击打开链接
1.Interface InvocationHandler
该接口中仅定义了一个方法:
Object invoke(Object proxy, Method method, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组(无参时设置为null)。
这个抽象方法在代理类中动态实现。
2.Proxy
该类即为动态代理类,其中主要包含如下内容:
protected Proxy(InvocationHandler h): 构造函数,用于给内部的invocation handler赋值。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) : loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)。
public interface Subject
{
public void doSomething();
}
public class RealSubject implements Subject
{
public void doSomething()
{
System.out.println( "call doSomething()" );
}
}
public class ProxyHandler implements InvocationHandler
{
private Object proxied;
public ProxyHandler( Object proxied )
{
this.proxied = proxied;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
//在转调具体目标对象之前,可以执行一些功能处理
//转调具体目标对象的方法
return method.invoke( proxied, args);
//在转调具体目标对象之后,可以执行一些功能处理
}
}
public class DynamicProxy
{
public static void main( String args[] )
{
RealSubject real = new RealSubject();
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class},
new ProxyHandler(real));
proxySubject.doSomething();
//write proxySubject class binary data to file
createProxyClassFile();
}
public static void createProxyClassFile()
{
String name = "ProxySubject";
byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );
try
{
FileOutputStream out = new FileOutputStream( name + ".class" );
out.write( data );
out.close();
}
catch( Exception e )
{
e.printStackTrace();
}
}
}
一个典型的动态代理创建对象过程可分为以下四个步骤:1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
静态代理与动态代理的异同点
两种代理的使用有一个限制:被代理对象必须实现一个接口,代理类只能代理接口中的方法。
不同在于,静态代理实现较为简单,但是编写接口中所有方法的实现,在接口中方法数量很多时,代理类的实现较为繁琐。动态代理则使用java反射功能,将所有方法的实现集中在一处,更加动态。
cglib:
上述所讲的动态代理和静态代理都要求被代理类实现某个接口,如果想对某个没有实现接口的类进行代理,则需要用到cglib库。
CGLIB是一个强大的高性能的代码生成包。
1>它广泛的被许多AOP的框架使用,例如:Spring AOP和dynaop,为他们提供方法的interception(拦截);
2>hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);
3>EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包。
它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以cglib包要依赖于asm包,需要一起导入。
package com.tech.cglibx;
public class BookServiceBean {
public void create(){
System.out.println("create() is running !");
}
public void query(){
System.out.println("query() is running !");
}
public void update(){
System.out.println("update() is running !");
}
public void delete(){
System.out.println("delete() is running !");
}
}
package com.tech.cglibx;
public class BookServiceFactory {
private static BookServiceBean service = new BookServiceBean();
private BookServiceFactory() {
}
public static BookServiceBean getInstance() {
return service;
}
}
public class Client {
public static void main(String[] args) {
BookServiceBean service = BookServiceFactory.getInstance();
doMethod(service);
}
public static void doMethod(BookServiceBean service){
service.create();
service.update();
service.query();
service.delete();
}
}
package com.tech.cglibx;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.log4j.Logger;
public class MyCglibProxy implements <span style="color:#ff0000;">MethodInterceptor</span>{
private Logger log=Logger.getLogger(MyCglibProxy.class);
public <span style="color:#ff0000;">Enhancer</span> enhancer = new Enhancer();
private String name;
public MyCglibProxy(String name) {
this.name = name ;
}
/**
* 根据class对象创建该对象的代理对象
* 1、设置父类;2、设置回调
* 本质:动态创建了一个class对象的子类
*
* @param cls
* @return
*/
public Object getDaoBean(Class cls) {
enhancer.setSuperclass(cls);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
log.info("调用的方法是:" + method.getName());
//用户进行判断
if(!"张三".equals(name)){
System.out.println("你没有权限!");
return null;
}
Object result = methodProxy.invokeSuper(object, args);
return result;
}
}
public static BookServiceBean getProxyInstance(MyCglibProxy myProxy){
Enhancer en = new Enhancer();
//进行代理
en.setSuperclass(BookServiceBean.class);
en.setCallback(myProxy);
//生成代理实例
return (BookServiceBean)en.create();
} <span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>
Spring AOP:点击打开链接
AOP概念
让我们从定义一些重要的AOP概念开始。
— 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
— 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
— 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
— 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。
— 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
— 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
— AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
— 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
各种通知类型包括:
— Around通知:包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为,它们负责选择继续执行连接点或通过返回它们自己的返回值或抛出异常来短路执行。
— Before通知:在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
— Throws通知:在方法抛出异常时执行的通知。Spring提供强制类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable或Exception强制类型转换。
— After returning通知:在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。