一、Spring的优点和弊端
1、Spring的优点
Spring是一个开源的免费的框架(容器)
Spring是一个轻量级的,非入侵式(新导入的jar包不会影响之前jar的正常使用)的框架
控制反转(IOC),面向切面编程(AOP)
支持事务的处理,支持框架整合
总结:Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
2、Spring Boot
一个快速开发的脚手架
基于SpringBoot可以快速地开发单个微服务
约定大于配置
3、Spring的弊端
(1)配置繁琐(下面是改进方向)
基于xml的配置
基于注解的组件扫描
(2)依赖繁琐
项目的依赖管理十分繁琐。在环境搭建时需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会阻碍项目的开发进度。
二、IOC理论推导
IOC,即控制反转,是Spring框架的核心内容。之前,程序是主动创建对象,控制权在程序员手上,使用IOC容器后程序不再具有主动性,而是变为被动地接受对象,系统的耦合度大大降低,可以更加专注在业务的实现上,这就是IOC的原型!
IOC不是一种具体的技术,而是一种设计思想,传统的应用程序使我们在类的内部显式的创建依赖的对象。从而导致类于类之间耦合度过高。而使用了IOC的设计思想,将对象的创建,查找依赖,以及生命周期的控制权交给了Ioc容器。对象之间耦合较松,更加灵活。
Spring容器在初始化的时候回去读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象,对象是Spring创建的。
总结:控制反转是一种通过描述(XML或注解),并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(DI)。一句话概况,所谓的IOC就是对象由Spring创建,管理和装配。
1、bean在bean.xml文件中声明
public class MyTest { public static void main(String[] args) { // 获取Spring的上下文对象 (Spring 容器) ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); // 获取bean person是id Person person = applicationContext.getBean("person",Person.class); System.out.println(person.toString()); } }
<bean id="person" class="com.xxx.xxx.Person">
<property name="str" value="Spring">
</bean>
<bean id="userDaoImpl" class="com.xxx.xxx.UserDaoImpl" />
<bean id="user" class="com.xxx.xxx.User" >
// 引用数据类型用ref
<property name="userDao" ref="userDaoImpl">
</bean>
2、IOC创建对象的方式
(1)使用无参构造创建对象,默认
(2)使用有参构造创建对象
①下标赋值
<bean id="user" class="com.xxx.xxx.User">
<constructor-arg index="0" value="yjh"> // 这里的0就是有参构造方法的第一个参数
</bean>
②类型赋值
<bean id="user" class="com.xxx.xxx.User">
// 如果有两个参数都是String,就不能成功赋值,该方法不建议使用
<constructor-arg type="java.lang.String" value="yjh">
</bean>
③参数名赋值
<bean id="user" class="com.xxx.xxx.User">
<constructor-arg name="name" value="yjh">
</bean>
总结:在配置文件加载的时候,容器中的对象就已经初始化了。
疑问:对象创建的整个过程,对象的生命周期,web请求的过程,在Spring中的路径。
对象别名
①使用alias
<bean id="person" class="com.xxx.xxx.Person">
<property name="str" value="Spring">
</bean>
//people就是person对象的别名
<alias name="person" alias="people">
②直接在bean中声明,people就是person的别名
<bean id="person" class="com.xxx.xxx.Person" name="people">
<property name="str" value="Spring">
</bean>
将其他xml配置文件用import导入,可以使用其他文件中声明的bean,减少重复代码,便于合作开发,加载的时候只需要一个xml文件,不需要加载其他文件。
// 会在src/main/resource中找到该文件
<import resource="beans1.xml">
三、DI 依赖注入
依赖:bean对象的创建依赖容器
注入:bean对象中的所有属性,由容器来注入
1、构造器注入
有参构造方法注入(上面有)
2、set注入
<bean id="address" class="com.xxx.xxx.Address" />
<bean id ="student" class="com.xxx.xxx.Student">
<property name="name" value="yjh" /> // 普通值注入
<property name="address" ref="address" /> // bean注入
<property name="books" > // 数组注入
<array>
<value>红楼<value>
<value>三国<value>
<array>
</property>
<property name="hobbys" > // list注入
<list>
<value>听歌<value>
<value>看书<value>
<list>
</property>
<property name="card" > // map注入
<map>
<entry key="a" value="1">
<map>
</property>
</bean>
3、p(property)命名空间
<bean id= "user" class = "com...User" p:name = "yjh" p:age="28"/>
4、c (constructor)命名空间
<bean id= "user" class = "com...User" c:name = "yjh" c:age="28"/>
p和c命名空间不能直接使用,需要在xml文件中导入约束。
四、bean的作用域
默认是单例模式(全局共享,唯一)
1、singleton(单例模式)
<bean id= "user" class = "com...User" c:name = "yjh" c:age="28" scope="singleton"/>
2、prototype(原型模式)
<bean id= "user" class = "com...User" c:name = "yjh" c:age="28" scope="prototype"/>
每次getBean的时候会创建一个新的对象
3、request、session、application
只能在web开发中使用
五、xml文件自动装配
□ 自动装配是Spring满足bean依赖的一种方式
□ Spring会在上下文中自动寻找,并自动给bean装配属性
例子:一个人两个宠物
// 自动装配代码块1
<bean id="cat" class="com.xxx.pojo.Cat"/>
<bean id="dog" class="com.xxx.pojo.Dog"/>
<bean id="person" class="com.xxx.pojo.Person">
// peron类的属性
<property name="name" value="Jack" />
<property name="dog" ref="dog" />
<property name="cat" ref="cat" />
</bean>
1、使用autowire="byName"进行自动装配
// 自动装配代码块2
<bean id="cat" class="com.xxx.pojo.Cat"/>
<bean id="dog" class="com.xxx.pojo.Dog"/>
<!---
byName:会自动在容器上下文中查找,和自己对象set方法中参数的变量名对应的bean
->
<bean id="person" class="com.xxx.pojo.Person" autowire="byName">
<property name="name" value="Jack" />
</bean>
两个代码块的功能是一样的,都是生成3个对象,人有两个宠物属性,代码块2将宠物类自动装配到人上了
2、使用autowire="byType"进行自动装配
// 自动装配代码块3
<bean id="cat" class="com.xxx.pojo.Cat"/>
<bean id="dog" class="com.xxx.pojo.Dog"/>
<!---
byType:会自动在容器上下文中查找,和自己对象set方法中参数的变量全限定名称相同的bean
->
<bean id="person" class="com.xxx.pojo.Person" autowire="byType">
<property name="name" value="Jack" />
</bean>
小结:在使用byName进行自动装配的时候,必须保证所有bean的id唯一,并且bean需要和自动注入的属性的set方法的值一致;在使用byType进行自动装配的时候,必须保证所有bean的class唯一,并且这个bean需要和自动注入的属性类型是一致的;
六、注解自动装配
@Autowired和@Resource
相同点
① @Autowired和@Resource的作用类似,表示被修饰的Java类需要注入对象,spring会自动扫描所有被两个标注的类,然后根据在IOC容器中找到匹配的类进行自动注入。
② 二者都可以用在属性和set方法上面,用在属性上面,则set方法可以去掉。
不同点
(1)@Autowired属于Spring注解,@Resource 属于Java注解。
(2)@Autowired默认按Type注入,当有多个相同类型的bean时,需要与@Qualifier(value="name")配合使用(byName),去查找唯一bean,找不到报错;@Resource同时支持byType和byName方式注入,可以指定查找方式,默认按byName方式注入,找不到则按byType注入,找不到报错。
(3)@Autowired只包含一个参数:required,标明是否开启自动注入,默认是true。而@Resource包含七个参数,最重要的两个参数是:name 和 type,用来指定注入的方式。
(4)@Autowired可以作用在:构造器、方法、参数、成员变量和注解上,@Resource可以作用在:类、成员变量和方法上。
七、Spring注解开发
1、bean
@Component
// @Component 等价于<bean id="user" class="com.xxx.xxx.User"/> @Component public class User { // @Value("kuang") 等价于<property name="name" value="kuang"/> @Value("kuang") private String name; }
2、属性注入
@Value
3、@Component衍生注解
在web开发中,会按照mvc三层架构分层
Dao(@Repository)
Service(@Service)
Controller(@Controller)
上面三个功能相同,注册bean,交于IOC容器托管
4、自动装配
@Autowired
@Resource
5、作用域
@Scope("prototype")
小结:
xml与注解
□ xml更加万能,适用于任何场合,维护简单方便
□ 注解不是自己类使用不了,维护相对复杂
xml注解的最佳实现
□ xml负责管理bean
□ 注解只负责完成属性的注入
八、使用Java的方式配置Spring
完全不使用xml配置,全权交给Java来做
// 被@Configuration修饰的类也会被IOC容器托管,注册到容器中,因为他本来就是一个@Component // 被@Configuration修饰的类代表这是一个配置类,相当于bean.xml文件,可以完成和bean.xml一样的功能,如扫描包 @Configuration // @ComponentScan 扫描包 @ComponentScan("com.zte.mds.web.test") public class Config { @Bean // @Bean 相当于<bean id="getUser" class="com.xxx.xxx.User"> // 方法名是bean对象的id // 返回值是bean对象的class public User getUser() { return new User(); } }
九、代理模式
为什么要学代理模式?代理模式是SpringAOP的底层!
代理模式的分类:①静态代理;②动态代理;
1、静态代理
角色分析:
□ 抽象角色:一般会使用接口或者抽象类来解决(租房)
□ 真实角色:被代理的角色(房东)
□ 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作(房屋中介)
□ 客户:访问代理对象的人 (我)
举个例子:房东和房屋中介都会实现租房接口,他们都有租房的方法,我们去租房的时候是不能访问房东对象的,而是去访问房屋中介的租房方法,房屋中介会在租房方法里面使用房东对象调用租房方法,在这行代码前后有其他方法,如看房和收钱方法。
代理模式的好处:①可以使真实角色的操作更加纯粹,不用去关注一些公共的业务;②公共业务交给代理角色,实现了业务的分工;③公共业务发生扩展的时候,方便集中管理;④ 不需要改动原有代码,不会改变原有真实角色的功能;
代理模式的缺点:一个真实角色就会有一个代理角色,代码量会翻倍,开发效率会变低;
2、动态代理
□ 动态代理和静态代理角色一样;
□ 动态代理的代理类(中介)是动态生产的,不是我们直接写好的;
□ 动态代理分为两大类:①基于接口的动态代理(JDK动态代理);②基于类的动态代理(cglib动态代理);
面试题:JDK动态代理和cglib动态代理的区别
需要了解的两个类:Proxy(代理)和InvocationHandler(调用处理程序)
// 租房接口(抽象角色) public interface Rent { public void rent(); }
// 房东 (真实角色) public class Host implements Rent{ @Override public void rent() { System.out.println("房东租房"); } }
// 调用处理程序 public class ProxyInvocationHandler implements InvocationHandler { // 被代理的接口 private Object target; public void setTarget(Object target) { this.target = target; } // 生成代理对象 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override // 处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { setHouse(); // 动态代理的本质就是使用反射机制实现! Object result = method.invoke(target,args); fare(); return result; } public void setHouse() { System.out.println("中介带看房子"); } public void fare() { System.out.println("收中介费"); } }
public class MyTest { public static void main(String[] args) { // 真实角色 Host host = new Host(); // 代理对象没有 ProxyInvocationHandler是调用处理器 ProxyInvocationHandler pih = new ProxyInvocationHandler(); // 设置要代理的对象 pih.setTarget(host); // 这里的代理对象是动态生成的,并没有去写 Rent proxy = (Rent) pih.getProxy(); proxy.rent(); } }
动态代理的好处:
□ 静态代理的好处都有
□ 一个动态代理类代理的是一个接口,一般对应的是一类任务,不是一个任务
□ 一个动态代理类可以代理多个类,只要是实现同一个接口即可
十、AOP
1、什么是AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
2、AOP中的基础概念
□ 横切关注点:跨越应用程序多个模块的方法或功能。与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全缓存,事务等;
□ 切面(Aspect):横切关注点被模块化的特殊对象,一个类;(Log类)
□ 通知(Advice):切面必须要完成的功能,类中的方法;(Log中的方法)
□ 目标(Target):被通知的对象。(真实角色)
□ 代理(Proxy):向目标对象应用通知之后创建的对象。(代理对象)
□ 切入点(PointCut):切面通知执行的“地点”的定义
□ 连接点(JointPoint):与切入点匹配的执行点
1)Spring中支持的5中类型的Advice
前置通知(方法前)实现MethodBeforeAdvice接口
后置通知(方法后)实现AfterAdvice或AfterReturnAdvice接口
环绕通知(方法前后)
异常抛出通知(方法抛出异常)
引介通知(类中增加新的方法属性)
applicationContext.xml
2)AOP的实现
①使用原生Spring API接口实现【主要是Spring API接口实现】;
②使用自定义类来实现AOP【主要是切面定义】
③使用注解实现
方法1、使用原生Spring API接口实现(Log和AfterLog是实现spring的接口)
<!--注册bean-->
<bean id="userServiceImpl" class="com.xxx.xxx.UserServiceImpl" />
<bean id="log" class="com.xxx.xxx.Log" />
<bean id="afterLog" class="com.xxx.xxx.AfterLog" />
<!--配置aop:需要导入aop约束-->
<aop:config>
<!--切入点:expression表达式,expression(要执行的位置!)-->
<aop:pointcut id="pointcut" expression="execution(* com.xxx.xxx.UserServiceImpl.*(..))" />
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
方法2、使用自定义类来实现AOP(DiyPointcut是自己写的类)
<!--注册bean-->
<bean id="diyPointcut" class="com.xxx.xxx.DiyPointcut" />
<!--配置aop:需要导入aop约束-->
<aop:config>
<!--自定义切面,ref=要引用的类-->
<aop:aspect ref="diyPointcut">
<!--切入点:expression表达式,expression(要执行的位置!)-->
<aop:pointcut id="pointcut" expression="execution(* com.xxx.xxx.UserServiceImpl.*(..))" />
<!--通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
<aop:aspect />
</aop:config>
方法3、使用注解方式实现
定义切面类
import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect // 标注这个类是一个切面 public class AnnotationPointCut { @Before("execution(* com.xxx.xxx.UserService.*(..))") public void before() { System.out.println("方法执行前"); } @After("execution(* com.xxx.xxx.UserService.*(..))") public void after() { System.out.println("方法执行后"); } }
对象的注入
<bean id="annotationPointCut" class="com.xxx.xxx.AnnotationPointCut"/>
<!--开启注解支持 JDK动态代理(默认 proxy-target-class="false") cglib动态代理(proxy-target-class="true"-->
<aop:aspect-autoproxy proxy-target-class="true"/>
十一、Spring中的事务管理
声明式事务
编程式事务