Java设计模式
Java反射技术
对象构建和方法的反射调用。在Java中,反射是通过包java.lang.reflect.*来实现的。
通过反射构建对象
随便创建一个类:
public class ReflectServiceImpl { ...... }
通过反射方法构建:方法反射生成对象
public ReflectServiceImpl getInstance() {
ReflectServiceImpl object = null;
try {
object = (ReflectServiceImpl)Class.forName(对应类的全限定名).newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
ex.printStackTrace();
}
return object;
}
类加载器注册了一个类ReflectServicelmpl 的全限定名,然后通过newInstance 方法初始化了一个类对象。
构造含有参数的类:
public class ReflectServiceImpl { private String name; public ReflectServiceimp(String name){ this.name= name; } public sayHellow(String name){...} ......}
通过反射生成带有参数的构建方法:
public ReflectServiceimpl getinstance( ) {
ReflectServiceimpl object = null ;
try {
object = (ReflectServiceimpl)Class.forName (对应类的全限定名).getConstructor(String.class).newinstance("张三");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
ex. printStackTrace () ;
}
return object;
}
先通过forName 加载到类的加载器。然后通过getConstructor方法,它的参数可以是多个,这里定义为String. class ,意为有且只有一个参数类型为String的构建方法。通过这个方法可以对重名方法进行排除,此时再用new Instance方法生成对象, 只是newInstance方法也多了一个参数“张三”而已。实际就等于object = new ReflectServicelmpl(“张三”),只是这里用反射机制来生成这个对象而已。反射的优点是只要配置就可以生成对象, 可以解除程序的稿合度,比较灵活。反射的缺点是运行比较慢。但是大部分情况下为了灵活度, 降低程序的稿合度,我们还是会使用反射的,比如SpringIoC容器。
反射方法
使用反射方法前要获取方法对象, 得到了方法才能够去反射。
public Object reflectMethod () {
Object returnObj = null ;
ReflectServiceimpl target= new ReflectServiceimpl();
try {
Method method = ReflectServiceimpl.class.qetMethod("sayHello", String.class);
returnObj = method.invoke(target ,"张三");
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
ex . printStackTrace();
}
return returnObj;
}
当有具体的对象target,而不知道具体是哪个类时,也可以使用target.getClass().getMethod(“sayHello”, String.class);代替它,其中第一个参数是方法名称,第二个参数是参数类型,是一个列表,多个参数可以继续编写多个类型,这样便能获得反射的方法对象。反射方法是运用retumObj = method.invoke(target ,”张三”);代码完成的,第一个参数为target,就是确定用哪个对象调用方法,而“张三”是参数,这行就等同于target.say Hello(“张三”);。如果存在多个参数, 可以写成Method.invoke(target, objl,obj2,obj3 … … ),这些要根据对象的具体方法来确定。
动态代理模式和责任链模式
动态代理的意义在于生成一个占位(又称代理对象),来代理真实对象,从而控制真实对象的访问。
在Java中有多种动态代理技术,比如JDK、CGLIB 、Javassist 、ASM ,其中最常用的动态代理技术有两种: 一种是JDK动态代理,这是JDK自带的功能;另一种是CGLIB,这是第三方提供的一个技术。目前, Spring 常用JDK 和CGLIB ,而MyBatis还使用了Javassist ,无论哪种代理其技术,它们的理念都是相似的。
JDK动态代理
JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象,所以先定义接口。
public interface HelloWorld { public vroid sayHelloWorld (); ... ... }
然后提供实现类HelloWordImpl来实现接口:
public class HelloWorldImpl implements HelloWorld { @Override public vroid sayHelloWorld(){... ...} ... ... }
在JDK动态代理中,要实现代理逻辑类必须去实现java.lang.reflect.InvocationHandler接口,它里面定义了一个invoke方法,并提供接口数组用于下挂代理对象。
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 代理对象
* @ param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Overrid
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 ;
}
}
第1步,建立代理对象和真实对象的关系。这里是使用了bind方法去完成的,方法里面首先用类的属性target保存了真实对象, 然后通过Proxy.newProxyInstance()建立并生成代理对象。
其中newProxyInstance方法包含3个参数。
· 第1 个是类加载器,我们采用了target 本身的类加载器。
· 第2 个是把生成的动态代理对象下挂在哪些接口下,这个写法就是放在target 实现的接口下。Hello Worldlmpl对象的接口显然就是HelloWorld代理对象可以这样声明: HelloWorld proxy = xxxx;
· 第3 个是定义实现方法逻辑的代理类, this表示当前对象,它必须实现InvocationHandler接口的invoke方法,它就是代理逻辑方法的现实方法。
第2 步,实现代理逻辑方法。invoke方法可以实现代理逻辑,invoke方法的3个参数的含义如下所示
• proxy ,代理对象,就是bind 方法生成的对象。
• method , 当前调度的方法。
• args , 调度方法的参数。
当我们使用了代理对象调度方法后,它就会进入到invoke方法里面。
Object obj = method.invoke(target, args);
这行代码相当于调度真实对象的方法, 只是通过反射实现而已。
测试:
public void testJdkProxy() {
JdkProxyExample jdk =new JdkProxyExample();
//绑定关系,因为挂在接口HelloWorld 下,所以声明代理对象HelloWorld proxy
HelloWorld proxy= (HelloWorld)jdk . bind(new HelloWorldimpl());
//注意,此时HelloWorld 对象己经是一个代理对象,它会进入代理的逻辑方法invoke里
proxy . sayHelloWorld();
}
CGLIB 动态代理
JDK 动态代理必须提供接口才能使用, 在一些不能提供接口的环境中,只能采用其他第三方技术, 比如CGLIB 动态代理。它的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。
public class CglibProxyExample implements Methodinterceptor {
/**
* 生成CGLIB代理对象
* @param els 一一Class 类
* @return Class 类的CG LIB 代理对象
*/
public Object getProxy(Class els) {
//CGLIB enhancer 增强类对象
Enhancer enhancer = new Enhancer() ;
//设置增强类型
enhancer.setSuperclass(cls) ;
//定义代理逻辑对象为当前对象,要求当前对象实现Methodinterceptor方法
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create() ;
}
/**
*代理逻辑方法
* @param proxy 代理对象
* @param method 方法
* @param args 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @ throws Throwable 异常
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.err.println("调用真实对象前);
//CGLIB 反射调用真实对象方法
Object result= methodProxy.invokeSuper(proxy, args);
System.err.println("调用真实对象后");
return result;
}
}
这里用了CGLIB的加强者Enhancer通过设置超类的方法(setSuperclass),然后通过setCallback方法设置哪个类为它的代理类。其中,参数为this就意味着是当前对象,那就要求用this这个对象实现接口Methodinterceptor 的方法一一intercept,然后返回代理对象。那么此时当前类的intercept 方法就是其代理逻辑方法。
测试:
public void tesCGLIBProxy() {
CglibProxyExample cpe =new CglibProxyExample();
ReflectServiceimpl obj = (ReflectServiceimpl)cpe.getProxy(ReflectServiceimpl).class);
obj.sayHello("张三");
}
拦截器
用JDK 动态代理来实现一个拦截器的逻辑,为此先定义拦截器接口Interceptor:
public interface Interceptor {
public boolean before (Object proxy , Object target, Method method, Object[] args);
public void around(Object proxy, Object target, Method method, Object[] args);
public void after(Object proxy, Object target, Method method, Object[] args);
}
这里定义了3 个方法,before、around、after方法,分别给予这些方法如下逻辑定义。
• 3 个方法的参数为: proxy 代理对象、target 真实对象、method 方法、args 运行方法参数。
• before 方法返回boolean 值,它在真实对象前调用。当返回为true时,则反射真实对象的方法:当返回为false时,则调用around方法。
• 在before 方法返回为false 的情况下,调用around方法。
• 在反射真实对象方法或者around 方法执行之后,调用after方法。
public class Myinterceptor implements Interceptor {
@Override
public boolean before (Object proxy, Object target, Method method, Object[] args) {
System.err.println("反射方法前逻辑");
return false ; //不反射被代理对象原有方法
}
@Override
public 飞roid after(Object proxy, Object target, Method method, Object[] args) {
System.err.println("反射方法后逻辑");
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.err.println("取代了被代理对象的方法");
}
}
在JDK 动态代理中使用拦截器:
public class InterceptorJdkProxy implements InvocationHandler {
private Object target; //真实对象
private String interceptorClass = null ; //拦截器全限定名
public InterceptorJdkProxy(Object target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
*绑定委托对象并返回一个【代理占位】
*
* @param target 真实对象
* @return 代理对象【占位】
* */
public static Object bind(Object target, String interceptorClass) {
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getinterfaces(), new InterceptorJdkProxy(target,interceptorClass));
}
@Override
/**
*通过代理对象调用方法,首先进入这个方法
*
* @param proxy 代理对象
* @param method方法,被调用方法
* @param args 方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (interceptorClass == null) {
//没有设置拦截器则直接反射原有方法
return method.invoke(target, args) ;
}
Object result = null;
//通过反射生成拦截器
Interceptor interceptor = (Interceptor)Class.forName(interceptorClass) .newinstance();
//调用前置方法
if (interceptor.before(proxy, target, method, args)) {
//反射原有对象方法
result = method.invoke(target, args);
} else {
//返回false 执行around 方法
interceptor.around(proxy, target, method, args);
}
//调用后置方法
interceptor.after(proxy, target, method, args);
return result;
}
这里有两个属性, 一个是target,它是真实对象;另一个是字符串interceptorClass ,它是一个拦截器的全限定名。解释一下这段代码的执行步骤。
第1 步,在bind 方法中用JDK 动态代理绑定了一个对象,然后返回代理对象。
第2 步,如果没有设置拦截器,则直接反射真实对象的方法,然后结束,否则进行第3 步。
第3 步,通过反射生成拦截器,并准备使用它。
第4 步,调用拦截器的before 方法,如果返回为true ,反射原来的方法;否则运行拦
截器的around 方法。
第5 步,调用拦截器的after 方法。
第6 步,返回结果。
测试结果:
public static void main(String[] args) {
HelloWord proxy = (HelloWord) InterceptorJdkProxy.bind(new HelloWorldimpl, "com.learn.ssm.chapter2.interceptor.MyInterceptor");
proxy.sayHelloWordld();
}
责任链模式
责任链模式:一个对象在一条链上被多个拦截器处理
befor方法按照从最后一个拦截器到第一个拦截器的加载顺序运行,而after方法则按照从第一个拦截器到最后一个拦截器的加载器运行。
责任链的优点在于可以在传递链上加入新的拦截器,增加拦截逻辑,其缺点是会增加代理和反射,而代理和反射的性能不高。
public class Interceptor implements Interceptor {
public void before(Object proxy, Object target, Method method, Object[] arg){
System.out.println("before方法");
}
public void around(Object proxy, Object target, Method method, Object[] arg){
}
public void after(Object proxy, Object target, Method method, Object[] arg){
System.out.println("after方法");
}
}
public static void main(String[] args){
HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.learn.ssm.chapter2.interceptor.Interceptor");
proxy.sayHelloWorld();
}
观察者模式
观察者模式又称为发布订阅模式,是对象的行为模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监视被观察者的状态,当被观察者的状态出现变化时,会通知所有观察者自动更新自己。
在Java中,需要去继承 java.util.Observable类。
被观察者的产品列表:
public class ProductLiust extends observable{
private List<String> productList = null; //产品列表
private static ProductList instance; //类唯一实例
private ProductList() {}; //构建方法私有化
//取得唯一实例
public static ProductList getInstance() {
if(instance == null){
instance = new ProductList();
instance.productList = new ArrayList<String>();
}
return instance;
}
//增加观察者
public void addProductListObserver(Observer observer){
this.addObserver(observer);
}
//新增产品
public void addProudct(String newProduct) {
productList.add(newProduct);
System.out.println("产品列表新增了产品:" + newProduct);
this.setChanged(); //设置被观察对象发生变化
this.notifyObservers(newProduct); //通知观察者,并传递新产品
}
}
1.构建方法私有化,避免通过new的方式创建对象,而是通过getInstance方法获得产品列表。
2.addProductListObserver:增加观察者。
3.addProudct:核心逻辑,在产品列表中增加一个产品,然后调用setChanged告知观察者发生了变化,notifyObservers方法,将新产品作为参数传递出去。
观察者需要实现 java.util.Observer接口的update方法。
public class JingDongObserver implements Observer{
@Override
public void update(Observable o, Object product){
String newProduct = (String) product;
System.out.println("产品同步");
}
}
测试
public static void main(String args){
ProductList observable = ProductList.getInstance();
JingDongObserver jdObserver = new JingDongObserver();
observable.addObserver(jdObserver);
observable.addProduct("新增产品");
}
工厂模式和抽象工厂
普通工厂(Simple Factory)模式
当一个接口拥有多个实现类,可以通过一个工厂管理他们的生成,通过输入特定的参数获得不同的实例对象。
public class ProductFactory {
public static IProduct createProduct(String productNo){
switch (productNo) {
case "1": return new Product1();
case "2": return new Product2();
}
}
}
抽象工厂(Abstract Factory)模式
抽象工厂模式可以向客户提供一个接口,使得客户端在不必指定产品的具体情况下创建多个产品族中的产品对象。
抽象工厂–>实例工厂–>具体实例对象
public interface IProductFactory {
public IProduct createProduct(String productNo);
}
createProduct方法是每一个实例工厂和抽象工厂都需要去实现的。
public class ProductFactory1 implements IProductFactory{
public IProduct createProduct(String productNo) {
public product = xxx; //工厂生产产品对象的规则,可以是一类产品的规则
return product;
}
}
建造者模式(Builder)
建造者模式属于对象的创造模式,可以将一个产品的内部表象(属性)与产品的生成过程分割开来,从而使一个建造过程具有不同的内部表象的产品对象。
构件旅游套票信息:旅游套票分为:成人票,儿童票,半价票
public class TicketHelper {
public void bulidAdult(String info) {
System.out.println("构件成人票逻辑: " + info);
}
public void bulidChilder(String info) {
System.out.println("构件儿童票逻辑: " + info);
}
public void bulidHalfPrice(String info) {
System.out.println("构件半价票逻辑: " + info);
}
}
建立一个构件类
public class TicketBuilder{
public static Object builder(TicketHelper helper){
System.out.println("通过TicketHelper建立套票信息");
}
}
使用
TicketHelper helper = new TickHelper();
helper.bulidAdult("成人票“);
helper.bulidChilder("有座儿童");
helper.bulidHalfPrice("半价票");
Object tick = TicketBuilder.builder(helper);