Spring : 春天 —>给软件行业带来了春天
为什么命名为“春天”,因为spring框架的出现,春天代表万物复苏,象征程序猿的好日子来了。
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。这就很离谱,我也想变成他一样的人。
Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术
官网 : http://spring.io/
官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/
GitHub : https://github.com/spring-projects
一.Spring框架介绍
1.概述
1.轻量级,开源的JavaEE框架。
2.解决企业开发的复杂性
3.两个核心部分 IOC(控制反转)AOP (面向切面)
2.特点
1.方便解耦,简化开发
2.AOP编程的支持
3.方便程序的测试,集成Junit
4.方便整合其他各种优秀的框架
5.声明事务的支持
6.降低JavaEE API的使用难度
7.Java源码是学习的经典案例
3.入门案例
1.创建Maven,引入Maven坐标 (spring-core)(spring-aop)(spring-context)(spring-expressio)(common-logging)(junit)
2.创建一个普通的类,在这类中创建一个普通的方法
3.创建spring配置文件bean.xml,在配置文件中配置创建的对象
4.创建测试类
二.IOC容器
1.定义
IOC为什么叫做”控制反转“,控制反转是面向对象编程的设计原则,可以减少计算机中代码的耦合度。其中最常见的方式叫做 DI(依赖注入)。目的就是把创建对象的过程交给Spring进行管理。
2.实现对象间调用的三种方式:
1.传统Java对象之间的调用
2.设计模式中的 ”工厂模式“ :工厂模式(三种)详解及源码_CSDNzgcxy的博客-CSDN博客
3.IOC底层原理实现的三种技术:xml解析、工厂模式、反射
IOC的过程
1.创建xml配置文件,配置创建的对象
<bean id="stu" class="spring.Student1"></bean>
2.创建反射类,通过反射机制创建对象
3.IOC接口的介绍
A.BeanFactory
IOC容器的基本实现,是spring内部的使用接口不提供给开发人员进行使用。
特点:加载配置文件时,不会创建对象,获取对象时才去创建对象。
BeanFactory beanFactory=new ClassPathXmlApplicationContext("bean.xml");//加载配置文件时不去创建对象
Student2 student2=(Student2) beanFactory.getBean("stu");//此时才开始创建对象
B.ApplicationContext
ApplicationContext是BeanFactory的子类,提供了更多更强大的功能(一代更比一代强),提供给开发人员使用
特点:加载配置文件时就会创建对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");//加载配置文件时就创建对象
Student2 student2=(Student2) applicationContext.getBean("stu");
C.ApplicationContext三大常用实现类分别是:
A、ClassPathXmlApplicationContext。(可以加载类路径下的配置文件。要求配置文件必须是在类路径下,不在类路径下加载不了)
B、FileSystemXmlApplicationContext。(可以加载磁盘任意路径下的配置文件,但是必须要有访问权限)
C、AnnotationConfigApplicationContext。(用于读取注解创建容器)
D.BeanFactory和ApplicationContext的区别:
BeanFactory:
①、 在构建核心容器时,采用延迟加载的方式创建对象。(即:什么时候根据ID获取对象了,什么时候才真正的创建对象)。
②、是Spring容器中的顶层接口。ApplicationContext:
①、 在构建核心容器时,采用立即加载的方式创建对象。(即:只要一读取完配置文件马上就创建配置文件中配置的对象)。
②、是BeanFactory的子接口。
4.IOC操作Bean管理
一.什么是Bean管理?
1、Spring创建对象
2、Spring注入属性
A.基于XML方式
1.基于XML方式创建对象
a、基本描述
在Spring配置文件中,使用Bean标签,标签里添加相应的属性,就可以实现对象的创建。
在Bean标签里包含了许多的属性
创建对象时,默认也是创造无参构造方法完成对象的创建
b、Bean标签常用的的几个属性介绍
1.id :给对象在容器中提供一个唯一标识。用于获取对象,不能添加特殊符号。
2.class:指定类的全限定类名。用于反射创建对象。指向classpath下类定义所在位置,默认情况下调用无参构造函数
3.name:name是bean的名称标识,在网上看到有的文章说name可以重复,但是我在Srping 4.0.4.RELEASE测试中,bean标签的name属性也是不能重复,且id和name属性也不能重复,name标签应该等同于id属性。
4.scope:指定对象的作用范围
- A、singleton: 单例的(默认值)。
- B、prototype:多例的。
- C、request:作用于web应用的请求范围。
- D、session:作用于web应用的会话范围。
- E、global session:作用于集群环境的会话范
5.init-method
init-method属性是bean的初始方法,在创建好bean后调用该方法。
6.destory-method
destory-method属性是bean的销毁方法,在销毁bean之前调用该方法,一般在该方法中释放资源
c、Bean的作用域及生命周期
(1)单例对象:scope="singleton"
<bean id="stu" class="spring.Student1" scope="singleton"></bean>
一个对象只有一个应用实例 ,作用范围就是整个引用。加载Spring配置文件时就会创建对象,默认为单例。
生命周期:
- 对象出生:当应用加载,创建容器时,对象就被创建了
- 对象活着:只要容器存在,对象就一直存在
- 对象死亡:当容器销毁或应用卸载,对象销毁
(2)原型对象:scope="prototype"
<bean id="stu" class="spring.Student1" scope="prototype">
每次访问对象时,都会重新创建对象实例,在调用getBean方法时就会创建一个多实例的对象
生命周期:
对象出生:当使用对象时,创建新的对象实例
对象活着:只要对象还在被使用,就一直存在
对象死亡:当对象长时间不使用,就会被Java垃圾回收站回收
d、工厂Bean(FactoryBean)
普通Bean:在配置文件定义的类型和返回类型一致
工厂Bean:在配置文件定义的类型可以和返回类型不一致
工厂Bean的实现:
首先先写一个Student的封装类,随便写几个属性
然后创建一个StudentFactoryBean去实现 AbstractFactoryBean这个接口
重写getObjectType、createInstance方法
public class StudentFactoryBean extends AbstractFactoryBean<Object>
{
@Override
public Class<?> getObjectType() {
return Student.class;
}
@Override
protected Object createInstance() throws Exception {
return new Student(1, "Ninja", "s90");
}
@Override
public String toString() {
return "StudentFactoryBean{}";
}
}
然后再写一个XML文件
<bean name="studentFactory" class="spring.StudentFactoryBean" />
最后写一个测试类测试一下
public class Bootstrap
{
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
new String[]{"Student.xml"}, false);
applicationContext.setAllowBeanDefinitionOverriding(false);
applicationContext.refresh();
// 生成工厂类生成的具体实体类,值的是studentFactory生成的类
Student student = (Student)applicationContext.getBean("studentFactory");
System.out.println(student.toString());
// 名字加了一个& 获取的是studentFactory本身
StudentFactoryBean studentFactoryBean = (StudentFactoryBean) applicationContext.getBean("&studentFactory");
System.out.println(studentFactoryBean.toString());
}
}
结果应该和大家预想的一致
Student{id=1, name='Ninja', classes='s90', rmark='null', list=null, map=null, prop=null, book=null}
StudentFactoryBean{}
讲到这里就得来讲一下Bean工厂和工厂Bean的区别了
BeanFactory是接口,提供了IOC容器最基本的形式,给具体的IOC容器的实现提供了规范,
FactoryBean也是接口,为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类.
区别:BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似
e、Bean的生命周期
定义:对象创建的过期到对象销毁的过程称为Bean的生命周期
生命周期过程:
1、通过构造器创建bean实例(无参构造)
2、调用set方法为bean设置属性
3、调用bean的初始化方法
4、bean对象获取使用
5、容器关闭,调用bean的销毁方法
Bean的生命周期(可怜只有七岁)
1、通过构造器创建bean实例(无参构造)
2、调用set方法为bean设置属性
3、把bean实例传递bean前置处理器方法postProcessBeforeInitialization
4、调用bean的初始化方法
5、把bean实例传递bean后置处理器方法postProcessAfterInitialization
6、bean对象获取使用
7、容器关闭,调用bean的销毁方法
2、基于XML方式注入属性
DI(依赖注入)实际就是注入属性
1、注入的方式
a、使用set方法注入
b、使用构造函数注入
c、Set方法注入的另一种方式,p命名空间(简化set属性注入的方式)
<bean name="studentFactory" class="spring.StudentFactoryBean" p:username="Ninja" /></beans>
2、注入空值和特殊符号
a、向属性值中设置控制
<property name="name" ><null /></property>
b、属性值中包含特殊符号
1.转义<>
2.CDATA
<property name="id">
<value>
<![CDATA[<<email@qq.com>]]>
</value>
</property>
3、注入属性-外部Bean
(1)创建UserService类和UserDao接口、UserDaoImp实现类
(2)在beanXml配置文件中创建对象UserService和UserDao,并且在UserService对象里注入UserDao属性
<!--创建对象UserService-->
<bean name="UserService" class="spring.user.UserService">
<!-- 注入外部bean ref属性是UserDaoImp对象的id-->
<property name="Userdao" ref="UserDaoImp"></property>
</bean>
<!--创建对象UserDaoImp-->
<bean name="UserDaoImp" class="spring.user.UserDaoImp"></bean>
</beans>
(3)创建测试类,执行测试方法得到结果。
4、注入属性-内部Bean
使用场景:一对多关系 举例:班级与学生的关系,在实体类中表示一对多关系
<bean id="Student" class="spring.Students">
<property name="name" value="Ninja"></property>
<property name="age" value="19"></property>
<property name="calsses">
<!--内部bean-->
<bean id="classes" class="spring.Classes">
<property name="name" value="IT90班"/>
</bean>
</property>
</bean>
5、注入属性-内部bean-级联值
(1)方法一
与上方相同
(2)方法二
<bean id="Student" class="spring.Students">
<property name="name" value="Ninja"></property>
<property name="age" value="19"></property>
<property name="calsses" ref="classes"></property>
</bean>
<bean id="classes" class="spring.Classes">
<property name="name" value="IT90班"></property>
</bean>
6、注入属性-集合属性
constructor先执行 property后执行 后执行的会把前面的覆盖 构造方法传参
使用迭代器逐个去取list和map里面的每个值
ref引用 引用类型 里面的被创建一次 外面的就会被创建一次
(1)注入数组类型属性
<property name="arrays">
<array>
<value>one</value>
<value>two</value>
<value>three</value>
</array>
</property>
(2)注入集合类型属性
<!--迭代器逐个去取list和map里面的每个值-->
<property name="list">
<list>
<value>上单</value>
<value>辅助</value>
<value>打野</value>
<value>下单</value>
</list>
</property>
(3)注入Map类型属性
<property name="map">
<map>
<entry key="1" value="nm"></entry>
<entry key="2" value="nm"></entry>
<entry key="3" value="nm"></entry>
<entry key="4" value="nm"></entry>
</map>
</property>
(4)注入Set类型属性
<property name="list">
<set>
<value>上单</value>
<value>辅助</value>
<value>打野</value>
<value>下单</value>
</set>
</property>
(5)集合里是对象属性
<property name="list">
<list>
<ref bean="对象"></ref>
<ref bean="对象1"></ref>
</list>
</property>
7、基于Xml自动装配属性
定义:根据指定装配规则,Spring将自动匹配的属性值进行注入
<!--对象注值 自动装配autowire constructor 构造方法 byname bytype-->
<bean id="teacher" class="spring.Teacher" autowire="byType"></bean>
8、基于Xml方式—引入外部文件
场景:spring配置JDBC数据连接文件的引入
创建Springw文件,引入context命名空间并且设置引入外部的配置文件
B.基于注解方式
1、什么是注解?
(1)代码里的特殊的标记 格式:@注解名称(属性名称=“属性值”)
(2)可以在类、方法、属性上添加注解
2、使用注解的目的?
(1)简化Xml配置,让程序变的优雅、简便
(2)打个比方:如果所有的bean创建都丢在Xml文件里,会使代码量变的非常庞大,繁琐,可读性差。
3、注解的运用
(1)创建对象提供的注解
1.@Componet:最普通的组件,可以注入到spring容器进行管理,用于标注一般类
2.@Service:作用服务层 服务器类的实现类
3.@Controller:作用于业务逻辑层 控制器类
4.@Repository:作用于持久层 数据访问层
(2)注入属性提供的注解
1.@Autowired(自动装配):
①、作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
②、如果IOC容器中没有任何bean的类型和要注入的变量类型匹配,则报错。位置:
可以是变量上,也可以是方法上。
③、如果IOC容器中有多个类型匹配时, 对比变量名, 自动注入和当前变量名一样的IOC容器中的对象,如果没有则报错。2.@Qualifier:
①、作用:在按照类中注入的基础之上, 再按照名称注入。
②、它在给类成员注入时不能单独使用。但是在给方法参数注入时可以。属性:value: 用于指定注入bean的id名称。可以配合Autowired3.@Resource:
①、作用:直接按照bean的id注入。它可以独立使用, 相当于Autowired和Qualifier的结合。②、属性:name:用于指定bean的id
③、注意:JDK8可以使用,JDK9不能使用
4.@Autowired和@Resource区别:
(1)@Autowired:自动按照类型type注入。
(2)@Resource:自动按照bean的id注入。可以独立使用,相当于Autowired和Qualifier的结合。
三、AOP
1、概念
面向切面编程。简单描述就是:将程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对自己的已有方法进行增强。无侵入式的不改变原有逻辑的情况下去增强添加逻辑,减少重复代码、提高开发效率、维护方便。
核心思想:在程序运行期间,在不修改源码的基础上对已有方法进行增强
什么是切面?
举个例子:要理解切面编程,就需要先理解什么是切面。首先确定西瓜的一个切入点,用刀把一个西瓜分成两瓣,切开的切口就是切面
2、AOP底层实现原理
使用动态代理技术:可以去看看我这篇比较详细 Java动态代理_CSDNzgcxy的博客-CSDN博客
(1)JDK实现动态代理(基于接口的动态代理)
(2)CGLIB实现动态代理(基于子类动态代理)
3、实践出真理
准备步骤:
(1)导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
(2)创建一个SpringAOP的包
(3)创建一个Xml文件
<!--SpringAOP扫描此包下面的类-->
<context:component-scan base-package="SpringAOP"></context:component-scan>
首先来举个栗子:某一天你要出去买东西,你要买一瓶“红茶” 是不是得做相应的步骤 称作为“原有的代码” 创建一个类 BuyRedTea
@Component//向容器声明这是一个bean 让容器管理
public class BuyRedTea
{
public void down(){
System.out.println("下楼..");
}
public void gotoMarket(){
System.out.println("去超市..");
}
public void buyRedtea(){
System.out.println("买红茶..");
int n=0/1; //决定是否正常执行 改成1/0不正常执行
}
public void pay() {
System.out.println("扫码付钱..");
}
public void up(){
System.out.println("上楼..");
}
}
然后你在买红茶之前又买了“绿茶” ,但又不能修改上面的逻辑。就得使用一个“前置增强”。然后老板这时候又给你发了工资,你买完红茶绿茶后又想安排一包华子,称为”后置增强“。那么此时买红茶这件事就被称为“切入点”,同时也可以使用一个环绕增强,在前置和后置前后增强。当出现异常就会返回异常,当你买完红茶后发现资金未到账就会出现不正常执行。具体如下:
@org.aspectj.lang.annotation.Aspect//声明一个切面类,因为类名与注解名一致产生全路径
@EnableAspectJAutoProxy//开启切面类的自动代理
@Component//向容器声明是bean 让容器管理
public class Aspect
{
//切入点的全路径
@Pointcut("execution(public void BuyRedTea.buyRedtea())")//全路径有参数就加上一个*到括号中
public void pointCat(){//用来绑定切入点的声明 用这个代替上面的方法
}
@Before("pointCat()")//前置增强 切入点之前执行的
public void before(){
System.out.println("买绿茶");
}
//切入点为buyRedtea买红茶的方法
@After("pointCat()")//后置增强 切入点后执行
public void after(){
System.out.println("买华子..");
}
//正常执行和不正常执行都是后置方法
@AfterReturning("pointCat()")//正常执行输出
public void normal(){
System.out.println("钱够");
}
@AfterThrowing("pointCat()")//不正常执行
public void w(){
System.out.println("钱不够");
}
@Around("pointCat()")//环绕增强
public Object around(ProceedingJoinPoint joinPoint)
{
try {
System.out.println("环绕前置");
//@Before
Object object=joinPoint.proceed();//调用byredtea
//@AfterReturning/@AfterThrowing
System.out.println("环绕后置");
return object;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕异常");
}
return null;
}
}
然后创建一个测试类来看一下效果
public class Run
{
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("Cig.xml");
BuyRedTea buyredTea= (BuyRedTea) context.getBean("buyRedTea");//拿到这个类 调用这个方法
buyredTea.down();
buyredTea.gotoMarket();
buyredTea.buyRedtea();
buyredTea.pay();
buyredTea.up();
}
}
①、JoinPoint(连接点):指那些被拦截到的点。(在Spring中这些点指的是方法,因为Spring只支持方法类型的连接点)。
②、PointCut(切入点):指的是要对哪些JoinPoint进行拦截的定义。
③、Advice(通知/增强):指拦截到JoinPoint之后所要做的事情。通知(Advice):实际增强的内容被称为通知
1.@Before: 前置通知, 在方法执行之前执行
2.@After: 后置通知, 在方法执行之后执行返回通知和异常通知都属于后置增强,在后置增强前执行。
3.@AfterRunning: 返回通知, 在方法返回结果之后执行 正常执行返回
4.@AfterThrowing: 异常通知, 在方法抛出异常之后 不正常执行返回
5.@Around: 环绕通知, 围绕着增强方法执行④、Target(目标对象):代理的目标对象。
⑤、Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入;
Aspect采用编译器织入和类装载期织入。
⑥、Aspect(切面):是切入点和通知的结合。
学习产生成果 成果改变生活