https://www.cnblogs.com/s648667069/p/6513335.html
https://blog.youkuaiyun.com/JIESA/article/details/78094677
https://blog.youkuaiyun.com/silyvin/article/details/70832415
https://blog.youkuaiyun.com/u013399093/article/details/54922902
https://blog.youkuaiyun.com/kirrin/article/details/47831845
http://www.icoolxue.com/album/show/158
http://edu.51cto.com/course/11708.html
https://blog.youkuaiyun.com/xunbaogang/article/details/52071532
https://blog.youkuaiyun.com/u013399093/article/details/54922902
https://blog.youkuaiyun.com/xiaohe958/article/details/49177615
待整理。。。
1. Bean的定义一定要有无参构造函数,先通过反射调用无参构造函数,再set属性。
2. <property name="xxxx" value = "yyy"/> xxx与setXXX()函数对应。
3. ApplicationContext:IOC容器,只有IOC容器初始化后才可以用于创建Bean,Spring提供两种容器,BeanFactory和ApplicationContext,前者面向Spring,后者面向用户,基本使用后者。
ApplicationContext继承关系:
|——ConfigurableApplicationContext
新加两个方法:refresh(), close()让ApplicationContext具有启动,刷新,关闭上下文的能力
|—— ClassPathXMLApplicationContext:类路径下加载容器
|——FileSystemXMLApplicationContext
getBean()方法是由BeanFactory提供,ApplicationContext继承BeanFactory
4. 参数注入两种该方式:<property name="" value=""> <constructor-arg value="" index=x type="int"> 后者通过index或type来指定调用什么构造函数
一些细节:
<constructor-arg type="int">
<value>1</value>
<index>1</index>
<constructor-arg/>
- 字面值:可以用字符串表示的值,可以通过<value>元素标签或value属性进行注入
- 基本数据类型及其封装、String等类型都可以用字面值注入的方式
- 若字面值中包含特殊字符,可以使用<![DCATA[]]>把字面值包裹起来。比如下面这个代码value的值为“<shanghai>”
<constructor-arg>
<value><![CDATA[<shanghai>]]></value>
</constructor-arg>
5. Bean的相互引用
<bean id="person" class="com.person">
<property name="name" value="tom"/>
<property name="owningCar" ref="car2" />
</bean>
<bean id="person" class="com.person">
<property name="owningCar">
<ref bean="car2"/>
</property>
</bean>
6. 内部Bean
<bean id="person" class="com.person">
<property name="name" value="tom"/>
<property name="owningCar">
<bean id="aCar" class="com.car">
<constructor-arg value=""/>
...
</bean>
</property>
</bean>
内部Bean可以不写Id,因为不可以被外部引用的。内部Bean也可以定义在constructor-arg的value属性里
7. 参数注入的null和级联参数:
null专有标记:<nulll/> 其实不赋值就是null
级联参数<constructor-arg ref="car2"/> <property name="car.maxSpeed" value=120/>
集合属性:<list><set>元素可以是<value><ref>或<bean>内部bean ,<map>的内部元素是<entry>
<property name="cars">
<list>
<ref bean="car1"/>
<ref bean="car2"/>
</list>
</property>
<property name="cars">
<map>
<entry key="A" value-ref="car1"/>
</map>
</property>
将集合配成一个Bean,共多个property使用
<!--需要导入util命名空间-->
<util:list id="cars">
<ref bean="car1"/>
<ref bean="car2"/>
</util:list>
<!--如何使用-->
<property name="cars" ref="cars">
</property>
8. <props>是<map>的子类,专用来为Properties类型属性赋值
public class DataSource {
priavte Properties properties;
...get set
}
<bean id="dataSOurce" class="com.DataSource">
<property name="properties">
<props>
<prop key="user">li</prop>
<prop key="age">2</prop>
</props>
</bean>
</bean>
9. 使用P命名空间为bean的属性赋值,需要先导入P命名空间。相对于传统的配置方式更加简洁。
<bean id="person" class="com.person" p.name="tom" p.age=20 p.cars-ref="cars"/>
10. 自动装配
在autowire属性指定自动装配模式:(1)byType (2)byName (3)byConstructor不推荐
<bean id="person" class="com.person" autowire="byName">
<!--byname要求插入的bean的名字和被插入的对象的setter风格的属性名一致-->
缺点:(1)所有属性要么都用,要么都不用autowire (2)byName,byType不可以混合使用
11. Bean的继承(配置继承)
<bean id="person1" class="com.person" p:age=10 p:name="tom"/>
<bean id="person2" class="com.person" parent="person1" name="jerry"/>
子Bean可以继承和覆盖父Bean的属性。
父Bean可以配置为实例或模板,模板不可以被实例化,举例:
<bean id="person" class="com.person" p:name="tom" abstract="true"/>
如果一个Bean的class属性没有指定,那么他必须是模板Bean
<bean id="template" p:name="tom" abstract="true"/>
12. 指定Bean的前置依赖
<bean id="" class="" depends-on="anotherBeanId"/>
前置依赖Bean会在这个Bean之前创建好,如果依赖到多个前置依赖Bean,则用逗号空格的方式设置Bean的名称。
13. Bean的作用域
Singleton:单例,容器初始化时创建
Prototype
14. 使用外部属性文件
应用背景:有时需要在Bean的配置里混入系统部署的细节信息,如文件路径等,而这些部署细节实际上需要和Bean配置相分离。
原理:Spring提供了一个PropertyPlaceholderConfiguregurer的BeanFactory后置处理器,这个处理器允许用户将Bean配置的部分内容外移到属性文件中。可以在Bean配置文件里使用${value}的变量,PropertyPlaceholderConfigurer从属性文件里加载属性,并使用这些属性来替换变量。
Spring还允许使用${propName},以实现属性之间的相互引用。
导入context命名空间<>
导入配置文件:
<context:property-placeholder location="classpath:db.properties"/>
使用外部变量名:
<property name="user" value="${aUserName}">
15. Spring表达式语言:SpEL
待整理
16. IOC容器中Bean的生命周期
IOC容器对Bean的生命周期进行管理的过程:
- 通过构造函数或工厂方法创建Bean的实例 ->constructor
- 为Bean的属性设置值和对其他Bean的引用 -> setXXX()
- 调用Bean的初始化方法 ->init-method
- Bean可以使用了
- 当关闭容器时,调用Bean的销毁方法 ->destory-method
16.1 使用init-method 和 destory-method
在Bean的声明里设置init-method和destory-method属性,为Bean指定初始化方法和销毁方法
<!--initCar destoryCar-->
<bean id="car" class="com.car" init-method="initCar" destory-method="destoryCar">
<property ...>
</bean>
PS:关闭容器使用ConfigurableApplicationContext.close() 方法
16.2 Bean后置处理器
Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理。 Bean后置处理器对IOC容器里的所有Bean实例逐一处理,而非针对单一实例。典型应该用是Bean属性的正确性或根据特定的标准更改Bean的属性。
要使用后置处理器,需要类实现org.springframework.beans.factory.config.BeanPostProcessor接口。在初始化方法被调用前后,Spring把每个Bean实例分别传递给上述接口的以下两个方法:
postProcessAfterInitialization(Object bean, String beanName);
postProcessBeforeInitialization(Object bean, String beanName);
在使用后置处理其之后,生命周期就变成:
- 通过构造函数或工厂方法创建Bean的实例 ->constructor
- 为Bean的属性设置值和对其他Bean的引用 -> setXXX()
- 将Bean实例传递给Bean后置处理器的postProcessBeforeInitialzation()
- 调用Bean的初始化方法 ->init-method
- 将Bean的实例传递给Bean后置处理器的postProcessAfterInitialization()
- Bean可以使用了
- 当关闭容器时,调用Bean的销毁方法 ->destory-method
使用:1定义public class MyBeanPostProcessor implements BeanPostProcessor;
2.声明Bean<bean class="com.MyBeanPostProcessor" > 不需要配置id,IOC容器自动识别是一个BeanPostProcesstor,这个Bean对每一其他的Bean都起作用,我们可以在MyBeanPostProcessor的代码逻辑里进行过滤。
17. 通过工厂方法配置Bean (创建Bean的第二种方法)
工厂方法分为静态工厂方法和实例工厂方法:
静态工厂方法:将对象创建的过程封装到静态方法中,当用户需要对象的时候,只需要简单的调用静态方法,而不用关心创建的细节。要声明通过静态方法创建的Bean,需要在Bean的class属性里指定拥有该工厂的方法的类,同时咋ifactory-method属性里指定工厂方法的名称,最后使用<contructor-arg>元素为该方法传递方法参数。
public class Car {
public Car(...)
}
//静态工厂方法:直接调用某一个类的静态方法,就可以返回Bean的实例
public class StaticCarFactory {
private static Map<String, Car> cars = new HashMap<>();
public static Car getCar(String name) {
return cars.get(name);
}
}
<!--通过静态工厂方法来配置Bean,注意不是配置静态工厂方法实例,而是配置Bean实例, class属性只想静态工厂方法的类名,factory-method指向方法名,constructor-arg指定工厂方法的参数-->
<bean id="car" class="com.StaticCarFatory" factory-method="getCar">
<constructor-arg value="audi"/>
</bean>
实例工厂方法:不是通过静态方法,是通过一个实例工厂类的方法构造。
// 实例工厂方法:实力工厂的方法,需要创建工厂本身的实例,再调用工厂的方法来放回Bean的实例
pulic class InstanceCarFactory {
private Map<String, Car> cars = new HashMap<>();
public InstanceCarFactory() {
car.put("audi", new Car(...));
}
public Car getCar(String brand) {
return car.get(brand); }
}
<!--现声明工厂的实例-->
<bean id="carFactory" class="com.InstanceCarFactory">
</bean>
<!--再声明被创建的Bean-->
<bean id="car" factory-bean="carFactory" factoyMethod="getCar">
<constructor-arg value="audi"/>
</bean>
18 通过FactoryBean创建Bean (创建Bean的第三种方式)
public class CarFactoryBean implements FactoryBean<Car>{
private String brand; public void setBrand(String brand){...}
@Override
public Car getObject() throws Exception {
return new Car(...);
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="car" class="com.CarFactoryBean">
<property name="brand" value="BMW"/>
</bean>
19. 通过注解
19.1使用注解配置Bean
-组件能够在classPath下扫描和实例化具有特定注解的组件
- 特定注解有:Component(通用组件) Controller(建议放在表现层)Service(建议放在服务层)Repository(建议放在持久层)。其实可以混用,因为Spring无法区分具体Bean是在哪个层。
- Bean的命名:默认是类名(第一个字母小写),也可以通过Value属性指定 @Component(value="xxx")
- 还需要配置Context component scan
<!--先要引入context命名空间-->
<context:component-scan base-package="com.test"/>
会扫描指定包及其子包。还有一个属性resource-pattern设定只扫描指定的类,比如:
resource-pattern="repository/*.class" 会指定只扫描com.test.repository包下的类
context:component-scan还有两种子节点<context:include-filter><context:excludefilter>
<context:exclude-filter type="annotation" expression="[Controller的全类名]"/>
上面表明exclude掉controller标记的类。
常见的type取值,前两种比较常用
类别 | 示例 | 说明 |
annotation | com.atgulug.xxxAnnotation | 所有标注了xxxAnnotation的类 |
assinable | com.atgulug.xxxService | 所有继承或扩展xxxService的类 |
aspectj | com.atgulug...*Service+ | 所有类名以Service结束的类及继承或扩展他们的类。该类型采用AspectJ表达式进行过滤 |
regex | com.\atguigu\.anno\.* | 所有com.atguigu.anno包下的类。该类型通过正则表达式进行类过滤 |
custom | com.atguigu.xxxTypeFilter | 采用xxxTypeFilter通过代码的方式定义过滤规则。该类必须实现org.springframework.core.type.TypeFilter接口 |
20 自动装配
<context:component-scan>元素还会自动注册 AutowiredAnnotationBeanPostProcessor实例,该实例可以自动装配具有@Autowired @Resource @Inject注解的属性
@Autowired可以放在构造函数,普通字段,以及任何有参数的地方。自动装配类型兼容的Bean
@Autowired(required=false)可以在没有对应的时候注入null
如何处理有多个匹配?@Autowired@Qualifier("name")指定要名为name的Bean
也可以加到入参的前面:
void setCar(@Qualifier("name") Car car)
@Autowired还可以放在list/set/map:对list IOC会将所有满足条件的都装配进去,对set IOC读取该集合的类型信息,然后装配所有满足类型的Bean;对于map,IOC会将Bean的名称作为键
@Resource注解要求提供一个Bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名称作为Bean的名称
@Inject和@Autowired一样,但没有required属性
21. AOP术语
切面(Aspect):横切关注点(跨越应用多个模块功能)被模块化的特殊对象
通知(Advice):切面必须要完成的工作
目标(Target): 被通知的对象
代理(Proxy): 向目标对下个应用通知之后创建的对象
连接点(JointPoint):程序执行的某个特定位置:如类的某个方法调用前、调用后、方法抛出异常后等。连接点有两个信息确定:方法表示的程序执行点;相对点表示方位。例如ArithmaticCalculator#add()方法执行前的连接点,执行点为ArithmaticCalculator#add();方位为该方法执行前的位置。
切点(pointcut):每个类都拥有多个连接点:例如ArithmeticCalculator的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。
22. AspectJ
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
// 把该类声明为一个切面:需要把该类放到IOC容器中,再声明为一个切面
@Aspect
@Component
public class LoggingAspect {
//声明该方法是一个前置advice:在目标方法之前执行
@Before("execution(public int com.atguigu.ArithmeticCalculation.*(int, int))")
public void beforeMethod(JoinPoint joinpoint) {
String methodName = joinpoint.getSigniture().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method begins" + methodName +"begins with " + args);
}
@AfterReturning(value = "execution(public int com.atguigu.ArithmeticCalculation.*(int, int))", returning = "result")
public void afterReturning(Jointpoint joinpoint, Object result) {
System.out.println("result is " + result);
}
@AfterThrowing(value = "execution(public int com.atguigu.ArithmeticCalculation.*(int, int))", throwing="ex")
public void afterThrowing(Jointpoint joinpoint, Exception ex) {
}
// 只有空指针异常才会触发这个方法
@AfterThrowing(value = "execution(public int com.atguigu.ArithmeticCalculation.*(int, int))", throwing="ex")
public void afterNullThrowing(Jointpoint joinpoint, NullPointException ex) {
}
// 环绕通知需要携带ProceedingJoinpoint类型的参数
// 环绕通知类似于动态代理的全过程: ProceedingJoinpoint参数可以决定是否执行目标方法
// 且环绕通知必须要有返回值,返回值即为目标方法的返回值
@Around("execution(public int com.atguigu.ArithmeticCalculation.*(int, int))")
public int aroundMethod(ProceedingJoinpoint joinpoint) {
System.out.println("around method");
Object result = null;
try {
// 前置通知
System.out.println("before proceed");
// 执行目标方法
result = joinpoint.proceed();
// 后置通知
System.out.println("after proceed");
} catch(Exception e) {
// 通知
System.out.println("after exception");
}
// 后置通知
System.out.println("method end");
return result;
}
}
<!--使切面的Before注解起作用(起作用的原理是:Spring自动为匹配的目标方法所在的类创建一个代理)-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(1) 加依赖包
(2)xml导入aop命名空间
(3)基于主借的方式
① 在配置文件中配置aop:aspectj-proxy
② 把横切关注点的代码抽象到一个切面类。ps:切面首先是一个Component,切面还需要加入@Aspect注解
③ 在类中声明各种通知:一个通知就是一个方法,声明通知类型包括:@Before,@After(无论该方法发生异常,且后置通知不能够访问到目标方法返回的结果,如果需要访问返回结果,需要使用返回通知),@AfterRunning, @AfterReturning(返回通知,发生在后置通知之后),@AfterThrowing,@Around。并需要指定切入点表达式
“execute(public void com.test.method(int, int))” | 当执行public void com.test.method(int, int)方法时 |
“execute(* com.test.method(int, int))” | 当执行任意修饰符,任意返回值,但方法名和参数列表匹配的方法的时候 |
“execute(public void com.test.*(int, int))” | 当执行com.test类中任意方法,且参数类型,修饰符和返回值类型满足的方法 |
“execute(public double com.test.*(double, ..))” | 第一个参数为double型,.. 表示陪陪任意数量任意类型的参数 |
④ 通知方法可以包含Jointpint类型的参数。
24. 切面的优先级
@Order
25. 重用切点表达式:
①:声明
@Pointcut("execute....")
public void declarePointcut();
②:使用
@Before("declarePointcut()") //如果是在不同的包,需要用"全类名.方法名()"
public void method()
26. 事务
- 事务管理是用来确保数据的完整性和一致性。
- 事务就是一系列动作,他们被当作一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
- 事务的四个关键属性(ACID) :① 原子性(Atomicity)事务是原子的,确保事务要么都完成,要么都不完成 ②. 一致性(consisitency)一旦所有事务动作完成,事务就被提交,数据和资源就处于一种满足业务规则的一致状态中 ③.隔离性(isolation)可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据破坏(四种隔离级别:https://blog.youkuaiyun.com/qq_33290787/article/details/51924963) ④.持久性(durability)一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。通常情况下,事务的结果被写到持久化存储中。
- 事务的传播行为:一个事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有的事务中运行,也可能开启另一个新的事务,并在自己的事务中运行。 @Transactional(propagation=Propagation.REQUIRED)
传播属性 | 描述 |
REQUIRED | 如果有事务运行,那么就用这个事务,否则就新建一个,并在自己的事务中运行 |
REQUIRED_NEW | 一定新开一个 |
SUPPORTS | 如果有事务运行,则当前方法运行在这个事务中,否则不运行在任何事务中。 |
NOT_SUPPORTED | 当前方法不运行在事务中,如果当前有运行的事务,则将该事务挂起。 |
MANDATORY | 当前方法必须运行在事务中,如果没有事务,则抛出异常。 |
NEVER | 当前事务不应该运行在事务中,如果当前有事务,则抛出异常 |
NESTED | 如果有事务在运行,当前的你发给发就应该在这个事务的嵌套事务内运行。否则就启动一个新的事务,并在他自己的事务中运行 |
public interface BookshopDao {
int findBookPriceByIsbn(String isbn);
// 更新库存-1
void updateBookStock(String isbn);
void updateUserAccount(String userName, int price);
}
@Repository("bookShopDao")
public class BookshopDaoImp implments BookshopDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
int findBookPriceByIsbn(String isbn){
jdbcTemplate.queryForObject(...);
}
// 更新库存-1
@Override
void updateBookStock(String isbn){
// 若库存不够,抛出异常
int stock = jdbcTemplate.queryForObject(...);
if (stock == 0) {
throw new RuntimeException();
}
jdbcTemplate.update(...);
}
@Override
void updateUserAccount(String userName, int price){
// 若余额不够,抛出异常
int balance = jdbcTemplate.queryForObject(...);
if (balance == 0) {
throw new RuntimeException();
}
jdbcTemplate.update(...);
}
}
@Service
public class BookshopService {
@Autowired
private BookshopDao bookShopDao;
@Transactional
public void purchase(String userName, String isbn) {
int price = bookShopDao.findBookPriceByIsbn(isbn);
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(userName, price);
}
}
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--启动注解事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>