第一节 简单工厂模式
- 不是23种设计模式的一种
- 工厂模式分为简单工厂模式,工厂方法,抽象工厂模式
- 工厂模式的好处:
工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。
利用工厂模式可以降低程序的耦合性,为后期的维护提供了很大的便利。
将选择实现类,创建对象同意管理和控制。从而将调用者跟我们的实现类解耦。
- 工厂与容器的概念:
容器存储的是一个大块的东西,工厂是用来生产的。
工厂主要用来生产对象,代替new。容器是用来存储对象的。
- 简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品的参数即可。但是工厂的职责过重,而且类型过多的时候不利于系统的扩展维护。
- 简单工厂的职责比较重,当类型过多的时候不利于系统的扩展维护。
简单代码示例:
package com.xiyou.shejimoshi.gongchangmoshi;
/**
* 简单工厂模式
*/
public class JianDanFactory {
public static void main(String[] args) {
Car aodi = CarFactory.createCar("奥迪");
Car jili = CarFactory.createCar("吉利");
aodi.run();
jili.run();
}
}
/**
* 汽车的抽象类
*/
interface Car{
public void run();
}
/**
* 奥迪汽车
*/
class AoDi implements Car{
public void run() {
System.out.println("奥迪车跑起来....");
}
}
/**
* 吉利汽车
*/
class JiLi implements Car{
public void run() {
System.out.println("吉利汽车跑起来....");
}
}
/**
* 汽车工厂
*/
class CarFactory{
/**
* 根据传进来的汽车名字,返回不同的汽车对象
* @param name
* @return
*/
public static Car createCar(String name){
if ("奥迪".equals(name)) {
return new AoDi();
}
if ("吉利".equals(name)) {
return new JiLi();
}
return null;
}
}
第二节 单例设计模式的总结
- 不推荐用双重检验锁的方式来创建单例(即使加上volatile禁止重排序也不推荐使用)
- 如何选择单例创建的方式?
如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举更好。
如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类更好。
- 在开发当中饿汉式用的多。
- 不要定义过多的常量,会造成内存溢出。
第三节 工厂方法设计模式
工厂方法模式,又称作多态性工厂模式,在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而具体创建的工作交给子类去做,该核心类称为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不用去管哪个产品类应当被实例化。
- 代码介绍:
package com.xiyou.shejimoshi.gongchangfangfa;
public class GongChangFangFa {
public static void main(String[] args) {
Car aodi = new AodiFactory().createCar();
Car jili = new JiLiFactory().createCar();
aodi.run();
jili.run();
}
}
/**
* 汽车的接口
*/
interface Car{
public void run();
}
/**
* 奥迪汽车
*/
class AoDi implements Car{
public void run() {
System.out.println("我是奥迪汽车");
}
}
/**
* 吉利汽车
*/
class JiLi implements Car{
public void run() {
System.out.println("我是吉利汽车");
}
}
/**
* 汽车工厂,生产汽车
*/
interface CarFactory{
public Car createCar();
}
/**
* 吉利汽车工厂
*/
class JiLiFactory implements CarFactory{
public Car createCar() {
return new JiLi();
}
}
/**
* 奥迪汽车工厂
*/
class AodiFactory implements CarFactory{
public Car createCar() {
return new AoDi();
}
}
第四节 抽象工厂模式
- 抽象工厂简单的说就是工厂的工厂,抽象工厂可以创建具体的工厂,由具体工厂来产生具体产品。
- 可以理解为工厂的工厂,不是一个产品簇下面的,又生产汽车(包括汽车A,B,C)又卖苹果(包括苹果A,B,C)。
- 代码如下:
package com.xiyou.mayi.thread5.chouxianggongchang;
/**
* 抽象工厂
*/
public class ChouXaingFactory {
public static void main(String[] args) {
CarFactory jiLi = new JiLi();
CarFactory aoDi = new AoDi();
jiLi.createChair().run();
jiLi.createEngine().run();
aoDi.createEngine().run();
aoDi.createChair().run();
}
}
/**
* 发动机的抽象
*/
interface Engine{
void run();
void start();
}
/**
* 发动机A
*/
class EngineA implements Engine{
@Override
public void run() {
System.out.println("发动机A的run");
}
@Override
public void start() {
System.out.println("发动机A的start");
}
}
class EngineB implements Engine{
@Override
public void run() {
System.out.println("发动机B的run");
}
@Override
public void start() {
System.out.println("发动机B的start");
}
}
/**
* 座椅的抽象类
*/
interface Chair{
void run();
}
/**
* 座椅A
*/
class ChairA implements Chair{
@Override
public void run() {
System.out.println("chairA的run");
}
}
/**
* 座椅B
*/
class ChairB implements Chair{
@Override
public void run() {
System.out.println("chairB的run");
}
}
/**
* 汽车工厂
*/
interface CarFactory{
// 创建发动机
Engine createEngine();
// 创建座椅
Chair createChair();
}
/**
* 吉利汽车
*/
class JiLi implements CarFactory{
@Override
public Engine createEngine() {
return new EngineA();
}
@Override
public Chair createChair() {
return new ChairA();
}
}
/**
* 奥迪
*/
class AoDi implements CarFactory{
@Override
public Engine createEngine() {
return new EngineB();
}
@Override
public Chair createChair() {
return new ChairB();
}
}
第五节 代理模式概述
-
通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或者调用后处理,即AOP。AOP的核心技术就是面向切面编程。
-
代理模式应用的场景:
SpringAop,事务原理,日志打印,权限控制,远程调用,安全代理 -
代理的分类:
(1)静态代理(静态定义代理类)
(2)动态代理(动态生成代理类)
(3)JDK自带动态代理(必须要有接口)
(4)CGLIB,Javaassist(字节码操作库) -
静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行之前就确定了。
不推荐使用,冗余度高,不适合扩展
- 举例说明:
/**
* JDK的静态代理
* 实现了业务的接口类,自己重写该方法,进行覆盖加前后操作
*/
public class JingTaiProxy implements IUserDao{
// 真实的实现类的对象
private IUserDao target;
JingTaiProxy(IUserDao userDao){
target = userDao;
}
/**
* 自己重写的方法,对其进行覆盖,加自己的逻辑
*/
public void save() {
System.out.println("执行save之前执行的方法...");
target.save();
System.out.println("执行save之后执行的方法...");
}
}
/**
* 接口类
*/
interface IUserDao{
void save();
}
/**
* 具体的实现类
*/
class UserDao implements IUserDao{
public void save() {
System.out.println("已经保存了数据...");
}
}
第六节 jdk的动态代理
- 动态代理
代理对象,不需要实现接口
代理对象的生成是利用了JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/ 目标对象实现的接口的类型)
动态代理也叫做JDK代理,接口代理
- JDK动态代理通过反射拿到代理对象,CGLIB动态代理通过ASM开源包,对代理对象类的class文件加载出来,通过修改字节码生子类来处理。
- jdk动态代理的原理:
是根据类加载器和接口创建代理类,该代理类是接口的实现类,所以必须使用接口,面向接口生成代理(jdk必须要接口的原因)
- 实现方式:
- 通过实现InvocationHandler接口创建自己的调用处理器DongTaiProxy handler = new DongTaiProxy (…)
- 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader, new Class[]{…})
- 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class})
- 通过构造函数创建代理对象,此时需要将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
- jdk动态代理的缺点是必须面向接口,目标业务类必须实现接口,因为我们创建的代理类实际上就是该接口的实现类,覆盖了其方法。
- 举例说明:
package com.xiyou.shejimoshi.dailimoshi.jdkProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK的动态代理
*/
public class DongTaiProxy implements InvocationHandler {
// 业务实现类的对象,用来调用具体的业务方法
private Object target;
// 通过构造函数传入目标对象
public DongTaiProxy(Object target){
this.target = target;
}
/**
* 我们虽然会调用业务方法,但是实际上调用了该方法
* 代理实际上不就是利用了多台的思想,调用的实际上是覆盖的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("调用开始处理。。。");
// 调用方法,反射调用target是实际的业务方法,args是参数
result = method.invoke(target, args);
System.out.println("调用结束。。。");
return result;
}
public static void main(String[] args) {
// 声明实际的对象
IUserDao userDao = new UserDao();
DongTaiProxy dongTaiProxy = new DongTaiProxy(userDao);
// 获取实际对象的类加载器
ClassLoader classLoader = userDao.getClass().getClassLoader();
// 得到实际对象的接口
Class<?>[] interfaces = userDao.getClass().getInterfaces();
// 生成代理对象,该代理对象不是我们生成的,是由jdk自动为我们生成的
IUserDao iUserDao = (IUserDao) Proxy.newProxyInstance(classLoader, interfaces, dongTaiProxy);
// 这时候虽然我们看着是在调用save方法,实际上我们调用的是代理中的invoke方法。
iUserDao.save();
}
}
第七节 cglib动态代理设计模式
- JDK使用的是反射的技术,而CGLIB使用的是asm字节码生成框架生产代理类的字节码文件。
- CGLIB并不需要委托类实现接口。而JDK的动态代理必须实现接口。所以CGLIB是针对类的,JDK是针对接口的。
- CGLIB是针对类实现的代理。主要是对指定的类生成一个子类,覆盖其中的方法(这样子才可以对指定类进行扩展性功能,在方法之前或者之后进行相关的处理)。因为是自动生成一个子类,所以该类或者方法不能生成为final,因为无法继承。
- 举例说明:
package com.xiyou.shejimoshi.dailimoshi.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGLIB的动态代理
*/
public class CglibProxy implements MethodInterceptor {
// 真实的对象,即被代理的对象
private Object targetObject;
/**
* 该方法的作用是生成代理对象
* 原理:
* 由CGLIB默认生成被代理对象的一个子类,回调返回该对象,创建子类对象
* @param targetObject
* @return
*/
public Object getInstance(Object targetObject){
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
// 设置其父类是被代理对象的类
enhancer.setSuperclass(targetObject.getClass());
// 回调设置成当前对象为代理对象
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 代理方法
* @param o
* @param method 具体的方法
* @param objects 参数
* @param methodProxy 代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行之前的方法...");
Object result = methodProxy.invoke(targetObject, objects);
System.out.println("执行之后的方法...");
return result;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao)cglibProxy.getInstance(new UserDao());
userDao.save();
}
}
/**
* 接口类
*/
interface IUserDao{
void save();
}
/**
* 具体的实现类
*/
class UserDao implements IUserDao{
public void save() {
System.out.println("已经保存了数据...");
}
}
补充:
CHLIB动态代理和JDK动态代理的区别:
- java动态代理是利用了反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理;而CGLIB动态代理运用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类进行处理。
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类,只能针对接口。而CGLIB针对类实现的代理,主要是对指定的类生成一个子类,覆盖其方法。
- 注意:因为核心就是集成,用子类的方法覆盖父类的方法,所以该类或者方法,不要声明final,final修饰的类无法集成,方法无法重写
- 在Spring中:
(1)如果目标对象实现了接口,默认会用JDK的动态代理实现AOP
(2)如果目标对象使用了接口,可以强制使用CGLIB实现AOP
(3)如果目标对象没有实现接口,必须采用CGLIB库,spring会自动在JDK和CGLIB之间转换。