Java 设计模式
Java反射技术
Java反射技术应用广泛,它能够配置:类的全限定名、方法和参数,完成对象的初始化,甚至是某些方法。这样就可以增强Java的可配置性。(Spring IoC的基本原理也是如此)。Java的反射内容繁多,包括对象的构建、反射方法、注解、参数、接口等。
通过反射构建对象
在下面的代码中,生成一个对象并将其返回。
public class ReflectServiceImpl {
public void sayHello(String name){
System.out.println("Hello" + name);
}
public ReflectServiceImpl getInstance(){
ReflectServiceImpl object = null;
try{
//给类加载器注册一个类ReflectServiceImpl的全限定名,然后通过newInstance方法初始化一个类对象
object = (ReflectServiceImpl)Class.forName("com.yanyi.exam.ReflectServiceImpl").newInstance();
}catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex){
ex.printStackTrace();
}
return object;
}
}
以上的代码中没有任何参数的类的反射生成,所以如果一个类的所有构建方法里都至少存在一个参数,如何使用反射构建?
先通过forName加载到类的加载器。然后通过getConstructor方法。它的参数可以是多个,在这里定义为String.class
表示有且只有一个参数类型为String的构建方法。通过这个歌方法可以对其他重名方法进行排除,此时再使用newInstance
方法生成对象。
import java.lang.reflect.InvocationTargetException;
public class ReflectServiceImpl2 {
private String name;
public ReflectServiceImpl2 (String name){
this.name = name;
}
public void sayHello(String name){
System.out.println("Hello" + name);
}
public ReflectServiceImpl2 getInstance(){
ReflectServiceImpl2 object = null;
try{
object = (ReflectServiceImpl2)Class.forName("com.yanyi.exam." +
"ReflectServiceImpl2").getConstructor(String.class).newInstance("阎懿");
/**
* 等效于 object = new ReflectServiceImpl2("阎懿")
* 在这里使用的是反射机制来创建
*/
}catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | NoSuchMethodException
| SecurityException | IllegalArgumentException
| InvocationTargetException ex){
ex.printStackTrace();
}
return object;
}
}
反射的有优点是只要配置就可以生成对象,可以解除程序的耦合度,比较灵活。其缺点是运行比较慢。
反射方法
在使用反射方法前要获取方法对象,得到了方法才能够去反射,当有具体的类对象target
,而不知道具体是哪个类,可以使用target.getClass().getMethod("sayHello", String.class)
去获取方法。然后使用invoke
去反射方法。
public Object reflectMethod() {
Object returnObj = null;
//创建类的对象
ReflectServiceImpl target = new ReflectServiceImpl();
try {
/**
* 若有具体的类对象`target`,而不知道具体是哪个类,使用如下方法去实现
* target.getClass().getMethod("sayHello", String.class)
*/
//获取方法 第一个参数是方法名称 第二个参数是参数类型
Method method = ReflectServiceImpl.class.getMethod("sayHello", String.class);
//反射方法
returnObj = method.invoke(target, "阎懿");
} catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
ex.printStackTrace();
}
return returnObj;
}
动态代理模式和责任链模式
动态代理的意义在于生成一个占位(又称代理对象),来代理真实对象,从而控制真实对象的访问。
代理的作用是:在代理对象之前或者之后加入对应的逻辑,或者根据其他规则控制调用者是否使用真实的对象。
我们需要在调用者调用对象之前产生一个代理对象,而这个代理对象需要和真实对象建立代理关系,所以代理分为两个步骤:
- 代理对象和真实对象间建立代理关系
- 实现代理对象的代理逻辑方法
JDK动态代理
JDK动态代理是java.lang,reflect.*
包提供的方法,它必须借助一个接口才能产生代理对象,所以先定义接口
public interface HelloWorld {
public void sayHelloWorld();
}
实现接口
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHelloWorld() {
System.out.println("HELLO WORLD");
}
}
实现java.lang,reflect.InvocationHandler
接口,其中定义了invoke
方法
package com.yanyi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyExample implements InvocationHandler {
//真实对象
private Object target = null;
/**
* 建立代理对象和真实对象的代理关系 并返回代理对象
* @param target 真实对象
* @return 代理对象
*/
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 代理方法的逻辑
* @param proxy 代理对象 bind方法生成的对象
* @param method 当前调度的方法
* @param args 当前方法参数
* @return 返回代理结果
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑的方法");
System.out.println("在调度真实对象之前的服务");
Object obj = method.invoke(target,args); //相当于调用SayHelloWorld方法
System.out.println("在调度真实对象之后的服务");
return obj;
}
}
- 建立代理对象和真实对象的关系
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
返回代理对象,newProxyInstance
方法包括了三个参数
- 第一个是类加载器,采用了target本身的类加载器。
- 第二个是生成的动态代理对象下挂在哪些接口下,这个写法就是放在target实现的接口下。
- 第三个是定义实现方法逻辑的代理类,this表示指向当前对象。
2. 实现代理逻辑方法
invoke可以实现代理逻辑
- proxy 是代理对象,是bind方法生成的对象
- method 当前调度的方法
- args 调度方法的参数
最后进行测试
import com.yanyi.proxy.Impl.HelloWorldImpl;
public class ProxyTest {
public static void main(String[] args) {
testJdkProxy();
}
public static void testJdkProxy() {
JdkProxyExample jdk = new JdkProxyExample();
HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
proxy.sayHelloWorld();
}
}