文章目录
Spring的核心是IoC和AOP,DI是依赖注入,是实现IoC的一种方式。
IoC的理解:即控制反转,将创建对象的权利反转给容器管理,消费者只需通过注入的方式拿到对象后使用。
AOP:即面向切面编程,对已有的方法进行增强,所有增强的代码放在一个类中成为切面,被增强的方法叫切点。一般可用于日志记录,事务管理等
配置Bean
配置的形式:基于xml;基于注解
Bean的配置方式:
-
通过全类名反射
<bean id="helloSpring" class="com.atguigu.test01.HelloSpring"> <property name="name" value="shenzhen"></property> </bean>
public class HelloSpring { private String name; public HelloSpring() { System.out.println("构造方法执行。。。"); } public void setName(String name) { System.out.println("赋值。。。"); this.name = name; } public String getName() { return name; } }
-
通过工厂方法(静态工厂方法&实例工厂方法)
静态工厂方法
<!-- car的实例,不是factory的实例 --> <bean id="car1" class="com.atguigu.test07.StaticFactory" factory-method="getCar"> <constructor-arg value="baoma"></constructor-arg> </bean>
public class StaticFactory { private static Map<String, Car> map = new HashMap<>(); static { map.put("audi",new Car("audi",100000)); map.put("baoma",new Car("baoma",200000)); } public static Car getCar(String name) { return map.get(name); } }
实例工厂方法
<bean id="cardFactory" class="com.atguigu.test07.InstanceFactory"></bean> <bean id="car2" factory-bean="cardFactory" factory-method="getCar"> <constructor-arg value="audi"></constructor-arg> </bean>
public class InstanceFactory { private Map<String, Car> map = new HashMap<>(); public InstanceFactory() { map.put("audi",new Car("audi",100000)); map.put("baoma",new Car("baoma",200000)); } public Car getCar(String name) { return map.get(name); } }
-
FactoryBean
<!-- FactoryBean配置Bean--> <bean id="car" class="com.atguigu.test08.CarFactoryBean"> <property name="brand" value="宝马"></property> </bean>
public class CarFactoryBean implements FactoryBean<Car> { private String brand; public void setBrand(String brand) { this.brand = brand; } @Override public Car getObject() throws Exception { return new Car(brand,1000000); } @Override public Class<?> getObjectType() { return Car.class; } @Override public boolean isSingleton() { return false; } }
依赖注入的方式:
- 构造注入
- 属性注入
- 构造方法注入(很少使用)
Spring中提供了两种IoC容器实现:
-
BeanFactory:IoC容器的基本实现
-
ApplicationContext :提供更多高级的特性,是BeanFactory的子接口
ApplicationContext 的主要实现类:
- ClassPathXmlApplicationContext:从类路径下加载配置文件
- FileSystemXmlApplicationContext:从文件系统中加载配置文件
ConfigurableApplicationContext是ApplicationContext的子接口,新增了两个主要的方法refresh()和close(),让ApplicationContext具有启动、刷新和关闭上下文的作用
ApplicationContext在初始化上下文时就实例化所有的单例bean。
WebApplicationContext是针对web应用的。
自动装配
autowird 指定装配类型,byType通过类型装配,byName通过名字装配
依赖关系
通过depends-on指定Bean的前置依赖
抽象Bean
Bean可以通过指定abstract=true设置为抽象Bean,抽象Bean不能被实例化
继承Bean
Bean可以通过parent继承Bean,会继承Bean的属性、类路径等
Bean的作用域
通过scope来指定作用域,取值有:
- singleton:单例,初始化spring容器的时候就创建了这个bean的实例
- prototype:多例,每次调用getBean都会创建
引入外部properties文件,设置数据库连接信息等
<!-- 引用外部的属性配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="datasource" class="com.atguigu.test06.DataSource" p:user="${user1}" p:password="${password}"
p:driver="${driver}" p:url="${url}"></bean>
Spring的表达式语言:SpEL
格式:value="#{xxxx}"
可以用来赋值一个引用的Bean,可以用来进行计算,可以调用函数使用T(类)
基于注解配置 Bean
以下注解要生效要配置包扫描,在xml中通过context:componet-scan配置扫描路径,多个用
,
隔开
-
@Component :标注这个注解的类将成为spring管理的组件
-
@Repository:标注持久层组件
-
@Service:标注业务层组件
-
@Controller:标注接口层组件
这些组件都可相互替代,只是为了让代码更加明晰建议按照规范使用
-
@Autowired:默认按照兼容类型自动注入Bean
可以用于字段、构造方法、所有一切具有参数的方法,设置required=false,当spring找不到对应的Bean注入时就不会报错。
-
@Qualifier:指定哪个Bean注入
-
@Resource:和@Autowired功能类似,可以指定要注入的Bean名称
-
@Inject:和@Autowired功能类似
如果@Resource和@Inject的属性设置为空,则默认按照变量名或方法名注入Bean
当使用@Autowire自动装配注入时,如果接口有多个实现类,可以通过两种方式指定哪个具体实现注入:
- 在实现类的组件注解上标注和当前变量名、方法名一致的Bean名称,例如@Repository(“repository”),例如private UserRepository repository
- 在@Autowired标注的字段上增加@Qualifier,指定要使用哪个实现类注入,例如@Qualifier("@userJdbcRepositoryImpl"),指定UserJdbcRepositoryImpl实现类注入UserRepository
泛型依赖注入
在父类中建立组合关系,并使用@Autowird注入,其子类继承各自的父类,那么一个子类就可以持有另外一个子类对象的引用
代码表示
public class BaseRepository<T> {
}
public class BaseService<T> {
@Autowired
protected BaseRepository<T> baseRepository;
public void add() {
System.out.println("add");
System.out.println(baseRepository);
}
}
@Repository
public class UserRepository extends BaseRepository<User> {
}
@Service
public class UserService extends BaseService<User> {
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-generic-di.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.add();
}
}
add
com.atguigu.test10.UserRepository@797badd3
Spring的AOP(面向切面编程)
需求:在某些方法的前后打印一些日志
方法:
1.直接使用动态代理
2.使用AOP
一些术语
-
切面(Aspect):
-
通知(Advice)
类型:前置通知、后置通知、返回通知、环绕通知、异常通知
-
目标(Target):
-
代理(Proxy):
-
连接点(Joinpoint)
-
切点
AOP实现方式:
-
基于AspectJ注解
@Aspect @Component public class CaculatorAspect { @Before("execution(* com.atguigu.test11.Caculator.*(int,int))") public void before(JoinPoint joinPoint){ String name = joinPoint.getSignature().getName(); List<Object> objects = Arrays.asList(joinPoint.getArgs()); System.out.println("方法:"+name+",参数:"+objects); } }
@Component public class Caculator { public int add(int i,int j) { return i+j; } public int sub(int i,int j) { return i - j; } }
<context:component-scan base-package="com.atguigu.test11"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
使用@Order可以指定切面的优先级,值越小优先级越高
使用@PointCut声明切入点表达式,实现表达式的复用
-
基于xml方式
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <beans> <bean id="caculator1" class="com.atguigu.test12.CaculatorTest"></bean> <bean id="caculatorAspect" class="com.atguigu.test12.CaculatorAspect"></bean> <!-- 配置AOP--> <aop:config> <!-- 配置切点表达式--> <aop:pointcut id="pointcut" expression="execution(* com.atguigu.test12.CaculatorTest.*(..))"/> <!-- 配置切面及通知--> <aop:aspect ref="caculatorAspect"> <aop:before method="before" pointcut-ref="pointcut"></aop:before> </aop:aspect> </aop:config> </beans> </beans>
Spring的事务管理
spring既支持编程式(xml)的事务,也支持声明式(注解)的事务管理
声明式事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<beans>
<context:component-scan base-package="com.atguigu.service"></context:component-scan>
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
</beans>
@Service
public class TxService {
@Autowired
private JdbcTemplate jdbcTemplate = null;
//事务注解
@Transactional
public void txTest(){
jdbcTemplate.update("update pms_brand set name=? where brand_id=?", "mm",1);
int i = 10/0;
jdbcTemplate.update("update test set salary = salary - ? where id=?", 400,1);
}
}
public class TestHello {
private ApplicationContext ctx = null;
private JdbcTemplate jdbcTemplate = null;
private TxService txService;
@Before
public void before(){
ctx = new ClassPathXmlApplicationContext("beans-jdbc.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
txService = (TxService)ctx.getBean("txService");
}
@Test
public void txTest(){
txService.txTest();
}
}
事务的传播行为
设置方式:@Transactional(propagation=Propagation.REQUIRED)
一个事务方法被另一个事务方法调用时,事务的处理行为
Spring支持的事务传播行为
传播属性 | 描述 |
---|---|
REQUIRED | 如果有事务在运行,就用当前的事务;如果没有事务,就新建一个事务。默认 |
REQUIRED_NEW | 当前的方法必须启动新事务,如果当前存在事务就将其挂起 |
SUPPORTS | 如果当前存在事务就在当前事务运行,没有则以非事务方式运行 |
NOT_SUPPORTS | 当前方法不应该运行在事务中,如果有事务就将其挂起 |
MANDATORY | 当前方法必须运行在事务中,如果没有事务就抛出异常 |
NEVER | 当前方法不应该在事务中运行,如果有事务则抛出异常 |
NESTED | 如果有事务在运行,当前方法就应该在这个事务的嵌套事务内运行,否则启动一个新事务,并在自己的事务内运行 |
事务的隔离级别
当多个事务对同一数据执行操作时会产生并发问题,需要设置事务的隔离级别防止并发问题的出现或者防止部分并发问题的出现,隔离级别越高,其并发问题的类型就越少,但其效率也会降低,最高级别的事务隔离级别是
串行化
,这就不会产生并发问题,但效率低下,所以得根据业务场景做权衡。并发问题的类型有:
脏读
事务T1,T2,T2读到了T1未提交的事务,然后T1又回滚了,T2读到了无效的数据。
不可重复读
事务T1,T2,T2读取了一个值,T1将其修改了并提交了事务,T2再次读取的时候值却变了
幻读
事务T1,T2,T2读取了若干行数据,T1插入了几行数据并提交了事务,T2再次读取的时候发现行数发生了变化
设置方式:@Transactional(isolation=Isolation.READ_COMMITED)
事务的隔离级别及可能的并发问题:
隔离级别 | 并发问题 |
---|---|
读未提交(Read Uncommited) | 脏读、不可重复读、幻读 |
读已提交(Read Commited) | 不可重复读、幻读 |
可重复读(Repeatable Read) | 幻读 |
串行化(Serializable) | 无 |
MySQL默认的隔离级别:Repeatable Read
除此之外@Transactional可以设置其他的属性:
- noRollBackFor:发生xxx异常时不回滚
- timeout:超过指定的时间,事务回滚,防止事务占用连接过长
编程式事务(基于xml)
todo
Spring 整合Hibernate
目标:
- 让IoC容器管理Hibernate的SessionFactory
- 让Hibernate使用Spring声明式事务
主要步骤:
1、Hibernate的主要配置文件只配置方言、生成表的方式、是否显示SQL等信息
2、Spring的配置:
1. 配置数据源
2. 配置SessionFactory,使用Spring提供的LocalSessionFactoryBean
3. 配置事务管理器
4. 配置事务属性
5. 配置事务的切点,把切点和事务属性结合起来
Spring 在Web中的应用
如何创建IOC容器?
在Web应用加载的时候,使用ServletContextListener方法监听应用的加载,然后把容器放在ServletContext域中