一、装配Spring Bean(Spring IoC)
Bean的装配有两种方式,xml方式和注解方式,Bean主要分为自己开发的类和引入的第三方的包。
自己开发的类可以采用@Component注解的方式或者xml的方式装配,推荐使用@Component注解的方式,因为注解功能更为强大,既能实现XML的功能,也提供了自动装配的功能。
引入第三方的包可以采用@Bean注解的方式或者xml的方式装配,推荐使用xml的方式。
1、通过注解装配Bean
1.1、使用@Component装配Bean
定义POJO:
@Component(value = "role")
public class Role{
@Value("1")
private Long id;
@Value("role_name_1")
private String roleName;
@Value("role_note_1")
private String note;
}
注解@Component代表String IoC会把这个类扫描生成Bean实例,@Value代表值的注入。
定义一个Java Config类:
@ComponentScan
public class PojoConfig{
}
@ComponentScan代表进行扫描,默认是扫描当前包的路径。
@ComponentScan也可以指定扫描某些包或者某些类:
//扫描指定类
@ComponentScan(basePackageClasses = {Role.class, RoleServiceImpl.class})
//扫描指定包
@ComponentScan(basePackages = {"com.ssm.chapter10.annotation.pojo", "com.ssm.chapter10.annotation.service"})
从IoC容器中获取并使用注解装配完成的Bean:
ApplicationContext context = new AnnotationConfigApplicationContext("PojoConfig.class");
Role role = context.getBean(Role.class);
context.close();
1.2、自动装配——@Autowired
用@Autowired注解后,spring会自动寻找合适类型的bean注入进来。
@Autowired装配属性:
@Autowired
private Role role = null;
有时候IoC容器会寻找失败,然后抛出异常,如果这个属性可有可无,寻找失败也无妨,可以修改为:
@Autowired(required = false)
private Role role = null;
@Autowired注解也允许方法配置:
@Autowired
public void setRole(Role role){
This.role = role;
}
@Autowired注解也允许带有参数的构造方法配置:
public RoleController2(@Autowired RoleService roleService){
this.roleService = roleService;
}
1.3、自动装配的歧义性
但是,如果一个接口有多个实现类,IoC容器会不知道应该注入哪个实现类,为了消除这种歧义性,有两个注解可以办到:
@Primary——优先注入:
@Component("roleService3")
@Primary
public class RoleServiceImpl3 implements RoleService{
......
}
@Primary代表如果存在多个RoleService接口的实现类,优先注入RoleServiceImpl3类的实例。
@Qualifier——按名称查找:
public class RoleController{
@Autowired
@Qualifier("roleService3")
private RoleService roleService = null;
}
@Qualifier代表这个属性不按照类型的方式注入,而是按照名称查找,将名为roleService3的实现类注入。
1.4、使用@Bean装配Bean
@Bean可以注解到方法之上,并且将方法返回的对象作为Spring的Bean,存放在IoC容器中:
@Bean(name = "role")
public Role getRole(){
return new Role;
}
2、通过XML配置装配Bean
2.1、装配简易值:
假设Source和JuiceMaker2都是已经创建好的类。
用xml装配:
<bean id="source" class="com.ssm.chapter9.pojo.Source">
<property name="fruit" value="橙汁">
<property name="sugar" value="糖">
<property name="size" value="大杯">
</bean>
<bean id="juiceMaker2" class="com.ssm.chapter9.pojo.JuiceMaker2">
<property name="beverageShop" value="贡茶">
<property name="source" ref="source">
</bean>
2.2、装配集合:
先创建一个包含集合属性的类:
public class ComplexAssembly{
private Long id;
private List<String> list;
private Map<String, String> map;
private Properties props;
private Set<String> set;
private String[] array;
}
装配集合类:
<bean id="complexAssembly" class="com.ssm.chapter10.pojo.ComplexAssembly">
<property name="id" value="1"/>
<property name="list">
<list>
<value>value-list-1</value>
<value>value-list-2</value>
<value>value-list-3</value>
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value-key-1"/>
<entry key="key2" value="value-key-2"/>
<entry key="key3" value="value-key-3"/>
</map>
</property>
<property name="props">
<props>
<prop key="prop1">value-prop-1</prop>
<prop key="prop2">value-prop-2</prop>
<prop key="prop3">value-prop-3</prop>
</props>
</property>
<property name="set">
<set>
<value>value-set-1</value>
<value>value-set-2</value>
<value>value-set-3</value>
</set>
</property>
<property name="array">
<array>
<value>value-array-1</value>
<value>value-array-2</value>
<value>value-array-3</value>
</array>
</property>
</bean>
如果集合之中包含的不是String,而是类的话,则List、Set、Array修改为:
<list>
<ref bean="role1"/>
<ref bean="role2"/>
</list>
Map修改为:
<map>
<entry key-ref="role1" value-ref="user1"/>
<entry key-ref="role2" value-ref="user2"/>
</map>
其中role1和user1都是类。
从IoC容器中获取并使用xml装配完成的Bean:
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
RoleService roleService = (RoleService)ctx.getBean("roleService");
3、加载属性文件
XML方式加载属性文件:
<beans ......>
<context:property-placeholder ignore-resource-not-found="true" location="classpath:database-config.properties"/>
</beans>
这里的ignore-resource-not-found属性代表是否允许文件不存在,配置为true代表允许文件不存在,这里的location是一个配置属性文件路径的选项,可以配置一个或多个文件,多个文件用逗号分割,多个文件时也可以按如下方法进行配置:
<beans ......>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 配置文件路径 -->
<property name="locations">
<array>
<value>classpath:database-config.properties</value>
<value>classpath:log4j.properties</value>
</array>
</property>
<property name="ignoreResourceNotFound" value="true"/>
</beans>
4、条件化装配Bean
当属性文件配置不完全时,条件化装配可以阻止其装配,避免装配失败导致返回异常。
@Conditional:
@Conditional({DataSourceCondition.class})
public DataSource getDataSource(
@Value("${jdbc.database.driver}") String driver,
@Value("${jdbc.database.url}") String url,
@Value("${jdbc.database.username}") String username,
@Value("${jdbc.database.password}") String password){
}
这里通过@Conditional引入了一个类——DataSourceCondition,由它来判断属性文件是否配置完全:
public class DataSourceCondition implements Condition{
@Override
//获取上下文环境
Environment env = context.getEnvironment();
//判断是否存在关于数据库的基础配置
return env.containsProperty("jdbc.database.driver")
&& env.containsProperty("jdbc.database.url")
&& env.containsProperty("jdbc.database.username")
&& env.containsProperty("jdbc.database.password")
}
5、Bean的作用域
在默认的情况下,Spring IoC容器只会为配置的Bean生成一个实例,而不是多个,有时候我们会希望获得多个实例,这是由Spring的作用域所决定的。
Spring的4种作用域:
- 单例(singleton):它是默认选项,在整个应用中,Spring只为其生成一个Bean的实例。
- 原型(prototype):每次注入或者通过Spring Ioc容器获取Bean时,Spring都会为它创建一个新的实例。
- 会话(session):在Web应用中使用,就是在会话过程中Spring只创建一个实例。
- 请求(request):在Web应用中使用的,就是在一次请求中Spring会创建一个实例。
代码示例:
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RoleDataSourceServiceImpl implements RoleDataSourceService{
}
6、Spring表达式(Spring EL)
二、面向切面编程(Spring AOP)
SpringAOP 是一种基于方法拦截的 AOP ,换句话说 Spring是一种只能支持方法拦截的 AOP。
Spring 中主要有两种方式去实现 AOP 拦截功能:
- 使用@AspectJ 注解驱动切面
- 使用 AspectJ 注入切面。
Spring AOP 的拦截方式中,真正常用的是用@AspectJ 注解的方式实现的切面,有时候 XML 配置也有一定的辅助作用。
1、使用@AspectJ注解开发 Spring AOP
1.1、选择切点
Spring 是方法级别的 AOP 框架,而我们主要也是以某个类的某个方法作为切点,用动态代理的理论来说,就是要拦截哪个方法织入对应的 AOP 通知。
创建接口:
public interface RoleService {
public void printRole(Role role) ;
}
创建接口的实现类:
@Component
public class RoleServiceimpl implements RoleService {
@Override
public void printRole (Role role) {
System.out.println("{id: " + role.getId() + "," + "role_name: " + role.getRoleName() + "note: " + role.getNote() + "}");
1.2、创建切面(拦截器)
定义切面:
@Aspect
public class RoleAspect{
@Before("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
public void before(){
}
@After("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
public void after(){
}
@AfterReturning("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
public void afterReturning(){
}
@AfterThrowing("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
public void afterThrowing(){
}
@Around("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
public void around(ProceedingJoinPoint jp){
try{
jp.proceed();
//先调度前置通知,接着反射切点方法,然后调度后置通知和返回通知
}catch(Throwable e){
e.printStackTrace();
}
}
}
在Spring中只要通过@Aspect注解一个类,那么Spring IoC容器就会认为这是一个切面了。
切面通过execution语句来确定要拦截的方法:
@Before("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
该正则表达式要在一个拦截器中书写多次,比较麻烦,可以使用@Pointcut解决:
@Aspect
public class RoleAspect{
@Pointcut("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..))")
public void print(){
}
@Before("point()")
public void before(){
}
@After("point()")
public void after(){
}
@AfterReturning("point()")
public void afterReturning(){
}
@AfterThrowing("point()")
public void afterThrowing(){
}
@Around("point()")
public void around(ProceedingJoinPoint jp){
try{
jp.proceed();
//先调度前置通知,接着反射切点方法,然后调度后置通知和返回通知
}catch(Throwable e){
e.printStackTrace();
}
}
}
如果需要给通知传递参数,execution语句可以修改为:
@Before("execution(*com.ssm.checpter11.aop.service.impl.RoleServiceImpl.printRole(..)) " + "&& args(role, sort)")
public void before(Role role, int sort){
}
1.3、生成动态代理类
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.ssm.chapter11.aop")
public class AopConfig(){
@Bean
public RoleAspect getRoleAspect(){
return new RoleAspect();
}
}
@EnableAspectJAutoProxy代表启用AspectJ框架的自动代理,@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的,作用为:配置spring容器(应用上下文)。
1.4、测试AOP流程
ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService = (RoleService)ctx.getBean(RoleService.class);
Role role = new Role();
roleService.printRole(role);
2、引入(对已有类进行增强,在已有类中引入新的接口)
2.1、定义新的接口
public interface RoleVerifier{
public boolean verify(Role role);
}
2.2、定义接口的实现类
public class RoleVerifierImpl implements RoleVerifier{
@Override
public boolean verify(Role role){
return role != null;
}
}
2.3、加入接口到切面类中
@DeclareParents(value="com.ssm.chapter11.aop.service.impl.RoleServiceImpl+", defaultImpl = RoleVerifierImpl.class)
public RoleVerifier roleVerifier;
该语句的含义是对RoleServiceImpl类进行增强,在RoleServiceImpl类中引入一个新的接口,而RoleVerifierImpl就是引入的新接口的实现类。
2.4、使用引入的加强功能
ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService = ctx.getBean(RoleService.class);
RoleVerifier roleVerifier = (RoleVerifier)roleService;
Role role = new Role();
if(roleVerifier.verify(role)){
roleService.printRole(role);
}
3、多个切面
当一个切点有多个切面时,默认情况下这些切面的执行没有任何顺序,不过我们可以通过给这些切面添加注解@Order()来确定顺序,示例如下:
@Aspect
@Order(1)
public class Aspect1{
......
}
三、MyBatis-Spring结合
1、简单应用
1.1、配置MyBatis
1.1.1、配置MyBatis配置文件
<configuration>
<settings>
...
</settings>
<typeAliases>
...
</typeAliases>
<mappers>
...
</mappers>
</configuration>
1.1.2、配置映射器
定义一个POJO:
public class Role{
private Long id;
private String roleName;
private String note;
}
定义映射器接口:
@Repository
public interface RoleMapper{
public Role getRole(Long id);
}
定义xml文件:
<mapper namespace="com.learn.ssm.chapter3.mapper.RoleMapper">
<select id="getRole" parameterType="long" resultType="role">
select id, role_name as roleName, note from t_role where id = #{id}
</select>
</mapper>
1.2、配置Spring
1.2.1、配置dataSource
<!--数据库连接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="username" value="root" />
<property name="password" value="123456" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/chapter12" />
</bean>
1.2.2、配置SqlSessionFactoryBean
<!--集成MyBatis-->
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--指定MyBatis配置文件-->
<property name="configLocation" value="classpath:sqlMapConfig.xml" />
</bean>
1.2.3、配置扫描方式加载映射器
<!--采用自动扫描方式创建mapper bean-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--设置要扫描的包-->
<property name="basePackage" value="com.ssm.chapter12.mapper" />
<property name="SqlSessionFactoryBeanName" value="SqlSessionFactory" />
<!--指定标注才扫描成为Mapper-->
<property name="annotationClass" value="org.springframework.stereotype.Repository" />
</bean>
这样配置以后Spring IoC容器将把包名为"com.ssm.chapter12.mapper",注解为@Repository的接口扫描为映射器对象。
1.3、使用
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
RoleMapper roleMapper = ctx.getBean(RoleMapper.class);
roleMapper.getRole(1);
2、数据库事务管理
2.1、MyBatis配置
在简单应用的基础上继续添加下列配置。
2.1.1、启用扫描机制,并指定扫描对应的包
<context:annotation-config />
<context:component-scan base-package="com.ssm.chapter13.*">
2.1.2、事务管理器配置数据源事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
2.1.3、使用注解定义事务
<tx:annotation-driven transaction-manager="transactionManager" />
2.2、配置服务类
2.2.1、定义接口
public interface RoleService{
public int insertRole(Role role);
}
public interface RoleListService{
public int insertRoleList(List<Role> roleList);
}
2.2.2、创建接口的实现类
@Service
public class RoleServiceImpl implements RoleService{
@Autowired
private RoleMapper roleMapper = null;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED)
public int insertRole(Role role){
return roleMapper.insertRole(role);
}
}
@Service
public class RoleListServiceImpl implements RoleListService{
@Autowired
private RoleService roleService = null;
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public int insertRoleList(List<Role> roleList){
int count = 0;
for(Role role : roleList){
count += roleService.insertRole(role);
}
return count;
}
}
@Service写在类的上面,标注将这个类交给Spring容器管理,spring容器要为他创建对象。
@Transactional注解,表示该服务类实现方法将会在对应的隔离级别和传播行为中运行。