2002 Rod Johnon <Expoer One-on-one j2eedevelopment and Design>
Spring 2003 ,IOC Aop
Spring data,spring boot,spring cloud,spring framework ,spring social
IOC :控制反转 (DI:依赖注入)
1.搭建Spring环境
下载jar
https://maven.springframework.org/release/org/springframework/spring/
spring-framework-4.3.9.RELEASE-dist.zip
开发spring至少需要使用的jar(5个+1个):
spring-aop.jar 开发AOP特性时需要的JAR
spring-beans.jar 处理Bean的jar <bean>
spring-context.jar 处理spring上下文的jar <context>
spring-core.jar spring核心jar
spring-expression.jar spring表达式
三方提供的日志jar
commons-logging.jar 日志
2.编写配置文件
为了编写时有一些提示、自动生成一些配置信息:
方式一:增加sts插件
可以给eclipse增加 支持spring的插件:spring tool suite(https://spring.io/tools/sts/all)
下载springsource-tool-suite-3.9.4.RELEASE-e4.7.3a-updatesite.zip,然后在Eclipse中安装:Help-Install new SoftWare.. - Add
方式二:
直接下载sts工具(相当于一个集合了Spring tool suite的Eclipse): https://spring.io/tools/sts/
新建:bean configuration .. - applicationContext.xml
3.开发Spring程序(IOC)
ApplicationContext conext = new ClassPathXmlApplicationContext("applicationContext.xml") ;
//执行从springIOC容器中获取一个 id为student的对象
Student student = (Student)conext.getBean("student") ;
可以发现,springioc容器 帮我们new了对象,并且给对象赋了值
SpringIOC发展史:
1.
Student student = new Student();
student.setXxx();
2.
简单工厂
3.ioc (超级工厂)
IOC(控制反转)也可以称之为DI(依赖注入):
控制反转:将 创建对象、属性值 的方式 进行了翻转,从new、setXxx() 翻转为了 从springIOC容器getBean()
依赖注入:将属性值 注入给了属性,将属性 注入给了bean,将bean注入给了ioc容器;
总结:ioc/di ,无论要什么对象,都可以直接去springioc容器中获取,而不需要自己操作(new\setXxx())
因此之后的ioc分为2步:1 先给springioc中存放对象并赋值 2 拿
DI:依赖注入 ,
Teacher
Course : cname teacher
IOC容器赋值:如果是简单类型(8个基本+String),value;
如果是对象类型,ref="需要引用的id值",因此实现了 对象与对象之间的依赖关系
conext.getBean(需要获取的bean的id值)
依赖注入3种方式:
1.set注入:通过setXxx()赋值
赋值,默认使用的是 set方法();
依赖注入底层是通过反射实现的。
<property...>
2.构造器注入:通过构造方法赋值
<constructor-arg value="ls" type="String" index="0" name="name"></constructor-arg>
需要注意:如果 <constructor-arg>的顺序 与构造方法参数的顺序不一致,则需要通过type或者index或name指定。
3.p命名空间注入
引入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
<bean id="course" class="org.lanqiao.entity.Course" p:courseHour="300" p:courseName="hadoop" p:teacher-ref="teacher">
简单类型:
p:属性名="属性值"
引用类型(除了String外):
p:属性名-ref="引用的id"
注意多个 p赋值的时候 要有空格。
注意:
无论是String还是Int/short/long,在赋值时都是 value="值" ,
因此建议 此种情况 需要配合 name\type进行区分
示例:
注入各种集合数据类型: List Set map properties
set、list、数组 各自都有自己的标签<set> <list> <array>,但是也可以混着用
给对象类型赋值null :
<property name="name" >
<null/> -->注意 没有<value>
</property>
赋空值 ""
<property name="name" >
<value></value>
</property>
在ioc中定义bean的前提:该bean的类 必须提供了 无参构造
自动装配(只适用于 ref类型 ):
约定优于配置
自动装配:
<bean ... class="org.lanqiao.entity.Course" autowire="byName|byType|constructor|no" > byName本质是byId
byName: 自动寻找:其他bean的id值=该Course类的属性名
byType: 其他bean的类型(class) 是否与 该Course类的ref属性类型一致 (注意,此种方式 必须满足:当前Ioc容器中 只能有一个Bean满足条件 )
constructor: 其他bean的类型(class) 是否与 该Course类的构造方法参数 的类型一致;此种方式的本质就是byType
可以在头文件中 一次性将该ioc容器的所有bean 统一设置成自动装配:
<beans xmlns="http://www.springframework.org/schema/beans"
...
default-autowire="byName">
自动装配虽然可以减少代码量,但是会降低程序的可读性,使用时需要谨慎。
使用注解定义bean:通过注解的形式 将bean以及相应的属性值 放入ioc容器
<context:component-scan base-package="org.lanqiao.dao">
</context:component-scan>Spring在启动的时候,会根据base-package在 该包中扫描所有类,查找这些类是否有注解@Component("studentDao"),如果有,则将该类 加入spring Ioc容器。
@Component细化:
dao层注解:@Repository
service层注解:@Service
控制器层注解:@Controller
1 使用注解实现事务(声明式事务)
目标:通过事务 使以下方法 要么全成功、要么全失败
public void addStudent()
{
//增加班级
//增加学生
//crdu
}
a. jar包
spring-tx-4.3.9.RELEASE
ojdbc.jar
commons-dbcp.jar 连接池使用到数据源
commons-pool.jar 连接池
spring-jdbc-4.3.9.RELEASE.jar
aopalliance.jar
b.配置
jdbc\mybatis\spring
增加事务tx的命名空间
<!-- 增加对事务的支持 -->
<tx:annotation-driven transaction-manager="txManager" />
c.使用
将需要 成为事务的方法 前增加注解:
@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
2.AOP:面向方面编程
一个普通的类 -> 有特定功能的类
a.继承类 b.实现接口 c.注解 d.配置
public class MyFilter exntends/implements Xx
{
}
类 -> “通知” :实现接口
前置通知实现步骤:
a.jar
aopaliance.jar
aspectjweaver.jar
b.配置
c.编写
aop:每当之前add()之前 自动执行一个方法log();
addStudent(); 业务方法(IStudentService.java中的 addStudent())
before(); 自动执行的通知,即aop前置通知
public class Xxx
{
@Test
a(){}
}
如果出现异常:类似java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
则说明缺少jar
后置通知:
a.通知类 ,普通实现接口
b.业务类、业务方法
StudentServiceImpl中的addStudent()
c.配置:
将业务类、通知 纳入springIOC容器
定义切入点(一端)、定义通知类(另一端),通过pointcut-ref将两端连接起来
异常通知:
根据异常通知接口的定义可以发现,异常通知的实现类 必须编写以下方法:
public void afterThrowing([Method, args, target], ThrowableSubclass):
a.public void afterThrowing(Method, args, target, ThrowableSubclass)
b.public void afterThrowing( ThrowableSubclass)
环绕通知: 在目标方法的前后、异常发生时、最终等各个地方都可以 进行的通知,最强大的一个通知;
可以获取目标方法的 全部控制权(目标方法是否执行、执行之前、执行之后、参数、返回值等)
在使用环绕通知时,目标方法的一切信息 都可以通过invocation参数获取到
环绕通知 底层是通过拦截器实现的。
二、实现注解实现 通知 ,aop
a.jar
与 实现接口 的方式相同
b.配置
将业务类、通知 纳入springIOC容器
开启注解对AOP的支持<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
业务类 addStudent - 通知
c.编写
通知:
@Aspect //声明该类 是一个 通知
public class LogBeforeAnnotation {
}
注意:通过注解形式 将对象增加到 ioc容器时,需要设置 扫描器
<context:component-scan base-package="org.lanqiao.aop"></context:component-scan>
扫描器 会将 指定的包 中的 @Componet @Service @Respository @Controller修饰的类产生的对象 增加到IOC容器中
@Aspect不需要 加入扫描器,只需要开启即可:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
通过注解形式 实现的aop,如果想获取 目标对象的一些参数,则需要使用一个对象:JointPoint
注解形式的返回值:
a.声明返回值 的参数名:
@AfterReturning( pointcut= "execution(public * addStudent(..))" ,returning="returningValue" )
public void myAfter(JoinPoint jp,Object returningValue) {//returningValue是返回值,但需要告诉spring
System.out.println("返回值:"+returningValue );
注解形式实现aop时,通知的方法的参数不能多、少
实现接口形式、注解形式 只捕获声明的特定类型的异常,而其他类型异常不捕获。
cath()
三、通过 配置将 类->通知
基于Schema配置
类似 与 实现接口的方式
接口方式通知:public class LogAfter implements AfterReturningAdvice
Schema方式通知:
a.编写一个普通类 public class LogAfter {}
b.将该类 通过配置,转为一个“通知”
如果要获取目标对象信息:
注解、schema:JoinPoint
接口:Method method, Object[] args, Object target
schema形式 和注解形式相似,不同之处: 注解形式 使用了注册@After, schmema形式进行了多余的配置
1.Spring开发Web项目 及 拆分Spring配置文件
a.Spring开发Web项目
Web项目如何初始化SpringIOC容器 :思路:当服务启动时(tomcat),通过监听器将SpringIOC容器初始化一次(该监听器 spring-web.jar已经提供)
因此用spring开发web项目 至少需要7个jar: spring-java的6个jar + spring-web.jar,注意:web项目的jar包 是存入到WEB-INF/lib中
web项目启动时 ,会自动加载web.xml,因此需要在web.xml中加载 监听器(ioc容器初始化)。
Web项目启动时,启动实例化Ioc容器:
<!-- 指定 Ioc容器(applicationContext.xml)的位置-->
<context-param>
<!-- 监听器的父类ContextLoader中有一个属性contextConfigLocation,该属性值 保存着 容器配置文件applicationContext.xml的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<!-- 配置spring-web.jar提供的监听器,此监听器 可以在服务器启动时 初始化Ioc容器。
初始化Ioc容器(applicationContext.xml) ,
1.告诉监听器 此容器的位置:context-param
2.默认约定的位置 :WEB-INF/applicationContext.xml
-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
b.拆分Spring配置文件
java项目:
applicationContext1.xml
applicationContext2.xml
applicationContext3.xml
ApplicationContext conext = new ClassPathXmlApplicationContext("applicationContext3.xml") ;
Web项目:
根据什么拆分?
i.三层结构
UI(html/css/jsp 、Servlet) applicationController.xml
Service :applicationService.xml
Dao:applicationDao.xml
公共 数据库:applicationDB.xml
ii.功能结构
学生相关配置 applicationContextStudent.xml <bean id="" class="X...Student">
班级相关配置 applicationContextClass.xml
合并:如何将多个配置文件 加载
(1)
<context-param>
<!-- 监听器的父类ContextLoader中有一个属性contextConfigLocation,该属性值 保存着 容器配置文件applicationContext.xml的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml,
classpath:applicationContext-Dao.xml,
classpath:applicationContext-Service.xml,
classpath:applicationContext-Controller.xml
</param-value>
</context-param>
(2)推荐
<context-param>
<!-- 监听器的父类ContextLoader中有一个属性contextConfigLocation,该属性值 保存着 容器配置文件applicationContext.xml的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml,
classpath:applicationContext-*.xml
</param-value>
</context-param>
(3)只在web.xml中加载主配置文件,
<param-value>
classpath:applicationContext.xml
</param-value>
然后在主配置问加中,加载其他配置文件
<import resource="applicationContext-*.xml"/>
Web项目:
Spring IoC容器?两种形式:
xml配置文件:applicationContext.xml
存bean: <bean id class>
取bean:
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");
context.getBean();
注解:带有@Configuration注解的类(配置类)
注意:两种形式获取的Ioc容器是 独立的
存bean
XXX
取bean
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class) ;
XXX:注解形式 给IoC容器中存放Bean:
1.必须有@Configuration注解(配置类)
2.形式:
①三层组件加入IOC容器: 给个各类加 注解 、 扫描器识别注解所在包
a.给三层组件 分别加注解(@Controller、@Service、@Repository -> @Component)
b.将注解所在包 纳入ioc扫描器(ComponentScan)
纳入ioc扫描器: ①xml配置文件 : <context:component-scan base-package="com.yanqun.controller" >
</context:component-scan>
逻辑: 在三层类上加注解 ,让ioc识别,扫描器
②注解扫描器
component-scan:只对三层组件负责
给扫描器指定规则 :
过滤类型:FilterType(ANNOTATION,ASSIGNABLE_TYPE,CUSTOM)
ANNOTATION:三层注解类型@Controller、@Service、@Repository -> @Component
excludeFilters:排除
includeFilters:有默认行为,可以通过useDefaultFilters = false禁止
ASSIGNABLE_TYPE:具体的类(StudentService.class)
区分:
ANNOTATION:Controller.clss 指的是 所有标有@Controller的类
ASSIGNABLE_TYPE:值得是具体的一个类 StudentController.class
CUSTOM自定义:自己定义包含规则
@ComponentScan.Filter(type= FilterType.CUSTOM ,value={MyFilter.class}
MyFilter implements TypeFilter 重写其中的match,如果return true则加入IoC容器
②非三层组件(Student.class 、IntToStringConver.class):
i. @Bean+方法的返回值 ,id默認就是方法名(可以通过@Bean("stu") 修改id值)
ii. import 、FactoryBean
bean: 类
a. 非三层组件 :Student\Teacher\配置信息 : @Bean
b. 三层组件 :Controller、Service、Dao :@Component(@Controller、@Service、@Repository)
三层组件
a.加入@Component等注解
b.配置,让容器识别注解
i.XML形式:通过扫描器 将@Component等注解所在包 扫描
<context:component-scan base-package="com.yanqun" >
</context:component-scan>
ii.注解
@Configuration
@ComponentScan(value="com.yanqun")
public class MyConfig {...}
component-scan指定规则:
FilterType :
ANNOTATION:指定类型 (@Compent @Controller @Service @Repository)
ASSIGNABLE_TYPE :自定义类的类中选 StudentDao
ASPECTJ,
REGEX,
CUSTOM:自定义形式 :a.@ComponentScan.Filter(type= FilterType.CUSTOM,classes ={ MyFilter.class })},useDefaultFilters = false)
b.MyFilter implements TypeFilter 重写match()方法,如果返回值true
---
@Component(@Controller、@Service、@Repository):三层组件
非三层组件
---
--bean的作用域
scope: singleton| prototype
执行时机(产生bean的时机):
singleton:容器在初始化时,就会创建对象(唯一的一个);以后再getBean时,不再产生新的bean。singleton也支持延迟加载(懒加载):在第一次使用时产生。 @Lazy
prototype:容器在初始化时,不创建对象;只是在每次使用时(每次从容器获取对象时 ,context.getBean(Xxxx)),再创建对象;并且 每次getBean()都会创建一个新的对象。
--条件注解 Spring Boot
可以让某一个Bean 在某些条件下 加入Ioc容器,其他情况下不加IoC容器。
a.准备 bean
b.增加条件Bean:给每个Bean设置条件 ,必须实现Condition接口
c.根据条件,加入IoC容器
--回顾给IoC加入Bean的方法
注解 :全部在@Congiration配置中设置:
三层组件: 扫描器 + 三层注解
非三层组件: ① @Bean+返回值
②@import
③FactoryBean(工厂Bean)
@import使用: ①直接编写到@Import中,并且id值 是全类名
②自定义ImportSelector接口的实现类,通过selectimports方法实现(方法的返回值 就是要纳入IoC容器的Bean) 。 并且 告知程序 自己编写的实现类。 @Import({Orange.class,MyImportSelector.class})
③编写ImportBeanDefinitionRegistrar接口的实现类,重写方法
@Import({Orange.class,MyImportSelector.class,ImportBeanDefinitionRegistrar.class})
--FactoryBean(工厂Bean)
1.准备bean。实现类和重写方法 2.注册bean。注册到@Bean中
注意:需要通过&区分 获取的对象是哪一个 : 不加&,获取的是最内部真实的Apple;
如果加了&,获取的 是FacotryBean
--IoC容器:初始化容器、..使用容器、销毁容器
---Bean的生命周期:创建(new ...)、初始化(赋初值)、 ....、销毁 (servlet)
方法一: Student.java
适用于:@Bean+返回值方式
init destroy
xml:
init-method="myInit" destroy-method="myDestroy"
注解:
@Bean(value="stu",initMethod = "myInit",destroyMethod = "myDestroy")
IoC容器在初始化时,会自动创建对象(构造方法) ->init ->.....->当容器关闭时 调用destroy...
方法二:
三层注解 (功能性注解、MyIntToStringConverter.java):@Controller、@Service、@Repository、@Component
-->三层注解(功能性注解【三层、功能性类】)
三层组件: 扫描器 + 三层注解(4个)
JAVA规范 :JSR250
1.将响应组件 加入 @Component注解、 给初始化方法加@PostConstruct、给销毁方法加@PreDestroy
@PostConstruct:相当于方法一的init
@PreDestroy:相当于方法一的destroy
如果要获取@Component注解中的bean,那么该Bean的名字就是@Component(value="xxx")的value值
方法三:两个接口
接口:适用于三层组件(扫描器+三层组件)
InitializingBean初始化
DisposableBean 销毁
初始化:只需要 实现InitializingBean中的afterPropertiesSet()方法
销毁:实现DisposableBean 中的destroy()方法
问题:要在SPring IOC容器中操作:操作方式 对象:Bean+返回 ,三层组件
->如果是注解形式 , 随便写一个方法 ,然后加上相应注解即可
如果是接口形式,必须 实现接口中规定的方法
方法四:(给容器中的所有Bean加初始化、销毁)一个接口
接口:适用于三层组件
接口BeanPostProcessor:拦截了所有中容器的Bean
---自动装配 : 三层组件(4个注册+扫描器)
@Autowired
Controller->Service->Dao
三层组件
通过@Autowired从Ioc容器中 根据类型自动注入(没有调用setXxx()方法)
-如果@Autowired在属性前标注,则不调用setXxx;如果标注在setXxx前面 ,则调用setXxx
-不能放在方法的参数前
@Autowired
private Xxx xx;
public void aa()
{
}
@Autowired
public void setXxx(xx xx)
{
}
Bean+返回值:
@Autowired 在方法的参数前(也可以省略)、方法前 (构造方法:特殊,如果只有一个有参构造方法,则构造方法前的@Autowired也可以省略)
public void setXxx(@Autowired xx xx)
{
}
之前:@Autowired 根据类型匹配:
三层注入方式/@Bean+返回值
1.如果有多个类型相同的,匹配哪个?
报错。 /默认值@primary
2.能否根据名字匹配?
可以,结合 @Qualifier("stuDao2")使用。
3.如果有0个类型相同,默认报错;可以修改成不注入(null),@Autowired(required=false)
自动注入方式一: @Autowired (Spring) ,默认根据类型
自动注入方式二 @Resource(JSR250),默认根据名字 (如果 有名字,根据名字匹配;如果没有名字,先根据名字查找,如果没找到,再根据类型查找);也可以通过name或type属性 指定根据名字 或类型找。
自动注入方式一: @Inject(JSR330),额外引入javax.inject.jar,默认根据类型匹配
---利用Spring底层组件进行开发 (三层组件)
能够供我们使用的组件,都是Aware的子接口,即XxxxAware
以ApplicationContextAware为例:实现步骤
a.实现ApplicationContextAware,
b.重写其中的方法,都包含了一个对象。只需要将该对象 赋值到属性中即可
有什么用:例如ApplicationContextAware,可以通过该接口 获取到Ioc容器对象。
执行时间: 如果在main()中new Ioc容器: 先执行ApplicationContextAware实现类中的方法,通过该方法传入IoC容器 供我们自己使用; 然后再将该容器通过new返回给用户
BeanNameAware:
--环境切换:@Profile
Spring:切换环境
激活方式一:
-Dspring.profiles.active=@Profile环境名
-Dspring.profiles.active=myApple
数据库环境:
@Profile
127.0.0.1 scott tiger
@Profile
192.168...
激活方式二:
硬编码
坑:错误写法
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class) ;
ConfigurableEnvironment environment = (ConfigurableEnvironment)context.getEnvironment();
environment.setActiveProfiles("myBanana");
其中AnnotationConfigApplicationContext中有一个refresh()操作:会将我们设置的一些参数还原
没激活 |->进行激活 ->刷新 ->没激活
流程调整:
没激活->进行激活 | ->刷新
什么时候设置 保存点|: 配置类的编写处
IoC容器在使用时必须refresh() ;如果是有参构造,内部已经刷新;如果无参构造,需要手工刷新。
---Spring重要组件BeanPostProcessor
接口BeanPostProcessor:拦截了所有中容器的Bean,并且可以进行bean的初始化 、销毁
创建->初始化->...-》销毁
BeanPostProcessor
BeanFactoryPostProcessor:拦截了容器
BeanDefinitionRegistryPostProcessor:即将被加载之前(解析之前,称为BeanDefination对象之前)
BeanDefinitionRegistryPostProcessor(a) -》加载bean->BeanFactoryPostProcessor(b)->实例化bean->BeanPostProcessor
同一个方法 在不同地方(类、接口)的出现时机问题:a继承b,因此a中必然包含b中的方法(记c ):虽然a和b中都有c,但是 因此c出现的时机不同, 则c的执行顺序也不同: 如果是在a中出现,则先执行;如果是在b中执行 则后执行
在同一个地方(类、接口),的不同方法的出现时机问题
监听器:
可以监听事件 ,监听的对象必须是 ApplicationEvent自身或其子类/子接口
方式一:
1必须实现ApplicationListener接口,
方式二:注解
(语法上 可以监听任意事件,但建议 ApplicationEvent自身或其子类/子接口)
Spring:要让SPring识别自己,必须加入IOc容器(Bean+返回值| 注解+扫描器)
自定被监听事件
a.自定义类 实现ApplicationEvent接口(自定义事件)
b.发布事件
context.publishEvent(自定义事件);
Quartz :定时异步任务
任务:做什么事情.... StudentService
触发器:定义时间
调度器:将任务、触发器 一一对应
实现步骤:(独立使用)
1.jar
2.
任务(Serivce..) :Job
3.测试方法:Job 、 触发器 、调度器
scheduler.shutdown():立刻关闭
scheduler.shutdown(false):shutdown()立刻关闭
scheduler.shutdown(true):将当前任务执行完毕后 再关闭
异步任务
触发器:CronScheduleBuilder
Cron表达式:
6-7个个参数,以空格隔开
秒 分 时 天(月) 月 天(周) (年)
2,10 3-6 12/3 L 2019-2022
--Spring整合Quartz
1.jar
spring基础包(spring-context-support.jar/spring-tx.jar)+quartz
2.
a.将Job信息封装到一个 实体类中
b.spring配置文件
调度器 ->触发器(Job、执行时间)