控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。
在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
constructor-arg:通过构造函数注入。
property:通过setter对应的方法注入。
<bean id="hello" class="com.lxl.pojo.Hello">
<constructor-arg name="name" value="Spring"/>
bean>
<import resource="{path}/beans.xml"/>
构造器注入(构造函数注入):constructor-arg
set注入:property
1、常量注入 2、Bean注入 3、数组注入 4、List注入 5、Map注入 6、set注入 7、Null注入 8、Properties注入
p命名和c命名注入 :p就是所谓的set注入 c就是所谓的构造器注入
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,
只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了
一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中
的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对
该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且
我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean
则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
1.在xml中显式配置;
<bean id="dog" class="com.lxl.pojo.Dog"/>
<bean id="cat" class="com.lxl.pojo.Cat"/>
<bean id="user" class="com.lxl.pojo.User">
<property name="Cat" ref="cat"/>
<property name="Dog" ref="dog"/>
<property name="name" value="qinjiang"/>
bean>
2.在java中显式配置;
3.隐式的bean发现机制和自动装配:例如如下所示,在user中不用再设置cat dog,因为在前面已经声明过bean了,我们让spring自动帮我们把前面声明过的bean进行装配
<bean id="dog" class="com.lxl.pojo.Dog"/>
<bean id="cat" class="com.lxl.pojo.Cat"/>
<bean id="user" class="com.lxl.pojo.User" autowire="byType">
<property name="name" value="qinjiang"/>
bean>
3.1)byName (按名称自动装配) 将查找其类中所有的set方法名,例如setCat,
获得将set去掉并且首字母小写的字符串,即cat。去spring容器中寻找是否有此字符串名称id的对象。
如果有,就取出注入;如果没有,就报空指针异常。
3.2)byType 同byName,不过byType的话同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
3.3)使用注解: 利用注解的方式注入属性。
使用注解准备工作:
1.在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2、开启属性注解支持!
<context:annotation-config/>
1.@Autowired: @Autowired是按类型自动转配的,不支持id匹配。
在user类中在对应属性上(cat dog)加@Autowired,然后配置文件为:
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
便可交给Spring自动装配
2.@Qualifier 使用@Autowired后可以使用@Qualifier根据byName的方式自动装配
如下:
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
3.@Resource;
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
其次再进行默认的byName方式进行装配;
如果以上都不成功,则按byType的方式自动装配。
都不成功,则报异常。
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
1、配置扫描哪些包下的注解
<context:component-scan base-package="com.lxl.pojo"/>
2、在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中
public class User {
public String name = "秦疆";
}
3、测试
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
1、可以不用提供set方法,直接在直接名上添加@value(“值”)
//@Component是为了标注一个类为Spring容器的Bean 把普通pojo实例化到spring容器中,相当于配置文件中的
@Component("user")
// 相当于配置文件中
public class User {
@Value("秦疆")
// 相当于配置文件中
public String name;
}
2、如果提供了set方法,在set方法上添加@value(“值”);
@Component("user")
public class User {
public String name;
@Value("秦疆")
public void setName(String name) {
this.name = name;
}
}
1、编写一个实体类,Dog
@Component //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
2、新建一个config配置包,编写一个MyConfig配置类
@Configuration //代表这是一个配置类
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,**方法名就是bean的id!**
public Dog dog(){
return new Dog();
}
}
3、测试
@Test
public void test2(){
ApplicationContext applicationContext =new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("dog");//通过Bean id获得
System.out.println(dog.name);
}
导入别的配置@Configuration //代表这是一个配置类
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public Dog dog(){
return new Dog();
}
}
思想:我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
静态代理角色分析
抽象角色 : 一般使用接口或者抽象类来实现
真实角色 : 被代理的角色
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
客户 : 使用代理角色来进行一些操作
有静态代理的所有好处
一个动态代理类可以代理多个类,只需生成一个抽象的接口即可,代理的是接口!
和代理的思想一致,
1).首先编写我们的业务接口和实现类
public interface UserService {
public void add();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加用户");
}
}
2).然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强:
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
3).最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .
<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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
4).测试
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}
自定义类来实现Aop 相较于第一种方法比较局限,因为切入类是我们预先写好的,我们并不能获取切入点方法的信息
1).写我们自己的一个切入类
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
2).然后在beans中配置
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<aop:config>
pointcut:切面
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
aop:aspect>
aop:config>
3).测试
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
和第二种类似,第二种是在配置文件中将某类标记为切面,然后再标记其方法为切点。使用注解实现是使用注解将该类标记为切面,然后将其方法标记为切点。
其中jar包包括单元测试、mybatis、mysql以及spring整个mybatis的包
junit
junit
3.8.1
test
junit
junit
4.12
org.mybatis
mybatis
3.5.2
mysql
mysql-connector-java
5.1.47
org.springframework
spring-webmvc
5.1.10.RELEASE
org.springframework
spring-jdbc
5.1.10.RELEASE
org.aspectj
aspectjweaver
1.9.4
org.mybatis
mybatis-spring
2.0.2
过程省略,创建User表内容为 id、name、pwd,id为主键。
User实例
public class User1 {
private int id; //id
private String name; //姓名
private String pwd; //密码
}
UserMapper:
public interface UserMapper {
public List selectUser();
}
UserMapper.xml
UserMapperImpl:
public class UserMapperImpl implements UserMapper{
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
我们原先在mybatis通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory,然后使用 SqlSessionFactory 来创建 SqlSession,在通过SqlSession对数据库进行操作。而且我们需要单独写一个配置类来进行这些操作,代码如下:
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
现在我们通过在spring配置文件中创建bean来实现这些操作,同时原先在mybatis的配置文件中我们需要连接数据源,先我们也在spring的配置文件中进行操作,只留一些基础的配置在mybatis配置文件中,例如settings、typeAliases之类的配置。
连接数据源
配置SqlSessionFactory
创建sqlSession及UserMapperImpl
在mybatis配置文件中只留一些基础的配置
然后spring的配置文件我们也可以分出来一份配置文件来存放一些固定不变的代码块,然后在总的配置文件中将其引入即可(import)。
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapper");
List user = mapper.selectUser();
for (User1 user1 : user) {
System.out.println(user1);
}
}
我们再来捋一下思路,先导入一些必要的jar包,然后创建pojo实体类与数据表中对应,然后创建UserMapper接口,接口中有我们要调用的方法,然后再创建UserMapper.xml文件实现对应的方法。然后写一个接口实现类UserMapperimpl,注意在类中创建一个参数SqlSession,在spring中注册时将SqlSession传进来,在UserMapperimpl中实现调用UserMapper的方法: returnsqlSession.getMapper(UserMapper.class).selectUser();
。而在spring的配置文件中则是要实现连接数据源以及创建SqlSession对象等操作。
与第一种方法类似,第二种方法的实现是在UserMapperImpl实现类中继承:SqlSessionDaoSupport类,然后直接调用其中的方法就可以省去在UserMapperImpl实现类中使用SqlSession的操作,相当于父类帮我们做了。
UserMapperImpl实现类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
}
可以看到没有SqlSession参数,而在spring中注册时不需要传入SqlSession传入sqlSessionFactory即可。
问题:我们在执行一个操作时,我们希望他要么全部成功要么全部失败而不是成功一部分。例如如下代码:
public List selectUser() {
User1 user = new User1(6,"小li2","123456");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(6);
return mapper.selectUser();
}
我们希望若执行delete失败的话add函数也执行失败,那么我们就需要添加事务声明,将selectUser()函数当作一个事务去处理。而Spring支持编程式事务管理和声明式的事务管理,我们本次只讲声明式事务,其是实现是将事务管理代码从业务代码中分离出来的,写在spring配置文件中。将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
通过配置事务管理器,我们知道需要对哪类事务进行管理,例如上面所述的JDBC事务,然后再配置这些事务的通知,也就是这些事务的特性,通过propagation属性实现。
propagation_required:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
我们使用如下代码进行测试,发现数据表中并没有插入数据,sql语句报错。
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapper2");
List user = mapper.selectUser();
System.out.println(user);
}
本博客是学习Spring的笔记记录,学习参考狂神说SSM框架系列:狂神说SSM框架系列