总结:对象实例化与初始化进⾏解耦
*/
public class Demo1 {
// scope="singleton" 默认的
@Test
public void test1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/l");
ParamAction p1 = (ParamAction) Bean("paramAction");
ParamAction p2 = (ParamAction) Bean("paramAction");
System.out.println(p1 == p2);
}
/
/运⾏结果:true
// scope="prototype"
@Test
public void test2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/l");
ParamAction p1 = (ParamAction) Bean("paramAction");
ParamAction p2 = (ParamAction) Bean("paramAction");
System.out.println(p1 == p2);
}
//运⾏结果:fasle
// 区别:单例多例的选择?
@Test
public void test3() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/l");
ParamAction p1 = (ParamAction) Bean("paramAction");
ParamAction p2 = (ParamAction) Bean("paramAction");
}
}
还有常⽤Spring中 @Repository、@Component、@Configuration @Service注解作⽤下的类默认都是单例模式的,所以,我⽬前认为在Spring下使⽤单例最优的⽅式是将类@Component注册为组件。
使⽤场景主要有:数据库配置、Redis配置、权限配置、Filter过滤、webMvcConfig、swagger及⾃定义的时间转换器、类型转换器、对接第三⽅硬件时,调⽤硬件的dll、so⽂件等。
单独使⽤@Component注解,只能控制到类上,使⽤@Configuration+@Bean可以控制到⽅法级别粒度,但是尽量避免@Component+@Bean组合使⽤,因为@Component+@Bean并不是单例,在调⽤过程中可能会出现多个Bean实例,导致蜜汁错误。
并不是所有的注解默认都是单例模式,@RestController就是多例
代理模式
Spring中的AOP就是典型的代理模式,⾸先我们先聊聊AOP(⾯向切⾯编程)的的⼀个设计原理:
AOP可以说是对OOP的补充和完善  OOP引⼊了封装、继承和多态性等概念来建⽴⼀种对象层次结构,⽤以模拟公共⾏为的⼀个集合。当我们需要为分散的对象引⼊公共⾏为的时候,OOP则显得⽆能为⼒。也就是说,oop允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如⽇志功能,⽇志代码往往⽔平地散布在所有对象层次中,⽽与它所散布到的对象的核⼼功能毫⽆关系。在OOP设计中,它导致⼤量代码重复,⽽不利于各个模块的重⽤。
实现AOP的技术,主要分为两⼤类:⼀是采⽤动态代理技术,利⽤截取消息的⽅式,对该消息进⾏装
饰,以取代原有对象⾏为的执⾏;⼆是采⽤静态织⼊的⽅式,引⼊特定的语法创建“⽅⾯”,从⽽使得编译器可以在编译期间织⼊有关“⽅⾯”的代码
简单点解释,⽐⽅说你想在你的biz层所有类中都加上⼀个打印‘你好’的功能,这时就可以⽤AOP思想来做,你先写个类写个类⽅法,⽅法经实现打印‘你好’,然后IOC这个类ref="biz.* "让每个类都注⼊即可实现。
package com.javaxl.design.lib;
import lib.proxy.Enhancer;
import lib.proxy.MethodInterceptor;
import lib.proxy.MethodProxy;
import lib.proxy.MethodProxy;
import flect.InvocationHandler;
import flect.Method;
import flect.Proxy;
SimpleDateFormat;
import java.util.Date;
class TeacherDao {
public String teach() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date()) + ":⽼师传授知识";
}
public TeacherDao sleep(int minutes) {
System.out.println("⽼师睡了" + minutes + "分钟");
return this;
}
}
//真实代理类的外⾐
class TeacherDaoProxy implements MethodInterceptor {
private Object target;
public TeacherDaoProxy(Object target) {
this.target = target;
}
//返回⼀个代理对象: 是 target  对象的代理对象
public Object getProxyInstance() {
//1. 创建⼀个⼯具类
Enhancer enhancer = new Enhancer();
//2. 设置⽗类
enhancer.Class());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建⼦类对象,即代理对象
ate();
}
/**
* @param proxyIns  由CGLib动态⽣成的代理类实例
* @param method    上⽂中实体类所调⽤的被代理的⽅法引⽤
* @param args      参数值列表
* @param methodProxy  ⽣成的代理类对⽅法的代理引⽤
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {        String methodName = Name();
Object res;
System.out.println("⽬标⽅法" + methodName + ":cglib代理开始...");
System.out.println("真实代理对象:" + Class());
System.out.println("⽬标对象:" + Class());
if ("sleep".equals(methodName)) {
//                            method.invoke(target, args);
//                            obj = proxy;
res = method.invoke(target, args);
//            res = methodProxy.invokeSuper(proxyIns,args);
res = proxyIns;
} else {
//                        proxy是真实代理类,method是⽬标⽅法,args是⽬标⽅法携带的参数
res = method.invoke(target, args);
res = method.invoke(target, args);
}
System.out.println("⽬标⽅法" + methodName + ":cglib代理结束...");
return res;
}
}
public class Client {
public static void main(String[] args) {
TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
proxy.sleep(111).sleep(222);
}
}
模板模式
定义:模板⽅法模式在⼀个⽅法中定义⼀个算法⾻架,并将某些步骤推迟到⼦类中实现。模板⽅法模式可以让⼦类在不改变算法整体结构的情况下,重新定义算法中的某些步骤
⽬的:1.使⽤模版⽅法模式的⽬的是避免编写重复代码,以便开发⼈员可以专注于核⼼业务逻辑的实现
2.解决接⼝与接⼝实现类之间继承⽭盾问题
以上定义来⾃《设计模式之美》
模板模式IOC容器初始化的时候就有所应⽤,我们可以看⼀下 AbstractApplicationContext 中的 refresh ⽅法,它就是⼀个模板⽅法,⾥⾯调⽤了⼀系列⽅法,有以实现的具体⽅法,有未实现的抽象⽅法,也有空的钩⼦⽅法。这⾥解释⼀下何为钩⼦⽅法,所谓钩⼦⽅法,就是在模板⽅法模式的抽象类中,我们可以定义⼀个⽅法,它默认不做任何事,⼦类可以视情况来决定是否重写它,该⽅法则被成为钩⼦⽅法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
//此⽅法内部调⽤了两个抽象⽅法refreshBeanFactory()和getBeanFactory()
//具体要取哪种beanFactory的控制权交给了⼦类
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
//钩⼦⽅法
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
/
/钩⼦⽅法
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
}
钩⼦函数应⽤场景:
public abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
if (callback())
method();
long end = System.currentTimeMillis();
System.out.println("当前⽅法执⾏时长:" + (end - start));
}
public abstract void method();
public boolean callback() {
return true;
spring ioc注解}
}
从上⾯可以看出:template⽅法默认是⽤作统计method⽅法的执⾏时长,但是有的时候我们⽆需统计代码时长,template函数中有⼀些其它逻辑要执⾏,在这⾥我们可以考虑采⽤钩⼦函数;钩⼦函数被⼦类覆写,覆写成false,那么method⽅法就不会被调⽤,不再统计代码时长了;前端框架Vue的⽣命周期就有多处⽤到钩⼦函数;
注意事项和细节
钩⼦函数 在模板⽅法模式的⽗类中,我们可以定义⼀个⽅法,它默认不做任何事,⼦类可以视情况要不要覆盖它,该⽅法称为“钩⼦”
算法只存在于⼀个地⽅,也就是在⽗类中,容易修改。需要修改算法时,只要修改⽗类的模板⽅法或者已经实现的某些步骤,⼦类就会继承这些修改
⼀般模板⽅法都加上 final 关键字, 防⽌⼦类重写模板⽅法
观察者模式

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。