spring总结
一、spring中的ioc
所谓的IOC一句话搞定:对象由Spring 来创建,管理,装配。ioc叫做控制反转,是spring的核心
ioc是一种思想 ,有一些实现方式,其中较为常用的一种是依赖注入(Dependency Injection,简称DI),依赖注入是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
实现方式,依赖注入,注入bean让spring去管理。默认bean的作用域是单例的singleton
依赖注入让 Spring 的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起。
spring中还提供了跟@Component等效的注解,通常情况下,我们会使用下面注解来代替@Component:
@Repository 用于对 DAO 实现类进行注解
@Service 用于对 Service 实现类进行注解
@Controller 用于对 Controller 实现类进行注解
@Autowired是按类型自动转配的,不支持id匹配。
需要导入 spring-aop的包!
1.1首先要在pom.xml中导入相关的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
1.2在resources目录下新建applicationContext.xml,并添加相关的约束,
注册bean后,这些类就由spring去管理了
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--方式一:配置版-->
<bean id="mysql" class="com.kuang.dao.UserDaoImplMysql"/>
<bean id="oracle" class="com.kuang.dao.UserDaoImplOracle"/>
<bean id="userService" class="com.kuang.service.UserServiceImpl">
<property name="userDao" ref="oracle"/>
</bean>
<!--方式二:注解版-->
<!--注解扫描-->
<context:component-scan base-package="com.kuang"/>
<!--自动配置注解,@Autowired-->
<context:annotation-config/>
</beans>
UserServiceImpl类
package com.kuang.service;
import com.kuang.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
/*public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}*/
public void getUser() {
userDao.getUser();
}
}
Userdao类
package com.kuang.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImplMysql implements UserDao {
public void getUser() {
System.out.println("MySQL获取用户数据!");
}
}
Test类
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.getUser();
//结果:最后打印出了MySQL获取用户数据!
}
总结:
方式一配置版:需要手动添加bean,并且UserServiceImpl类中要有一个属性UserDao接口,还有有set方法。
方式二注解版:要添加一个注解扫描,自动配置的支持,这样UserServiceImpl就不需要有set方法了,直接在属性
上添加一个注解@Autowired,和在实现类上添加@Service(“userService”)注解,在Userdao类中要添加注解@Repository,
注意:使用注解开发时在测试类中获取bean的时候,如果@Service() 注解如果没有指定bean的名字,默认为小写开头的类名。例如类名是UserServiceImpl,则spring返回userServiceImpl的bean名。 不然控制台会报错没有找到bean
二、使用java配置类@Configuration
先创建一个类,MyConfig类,在类上面添加注解@Configuration,代表这是一个配置类,也会被spring托管,因为它本来就是一个@Component,等价于之前的applicationContext.xml
@ComponentScan(“com.kuang”)等价于之前的 <context:component-scan base-package=“com.kuang”/>
bean可能有多个,使用@Import(Config2.class)导入到一个配置类中使用
@Configuration //代表这是一个配置类
@ComponentScan("com.kuang")
@Import(Config2.class)
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public UserService getUserService() {
return new UserServiceImpl();
}
}
三、动态代理
动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。动态代理与静态代理原理是一样的,只是它没有具体的代理类,直接在程序运行时动态生成了一个代理对象。
动态代理分为两大类:
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib
主要使用JDK原生的动态代理,里面主要有两个类:
-
Proxy.newProxyInstance():产生代理接口的实例。
-
ClassLoader:类加载器。即被代理的接口的类加载器。
-
Class[] interface:代理类实现的接口(房东租房那个接口)
-
InvocationHandler:将要在代理中实现的功能写在该对象中
-
InvocationHandler中的invoke方法:调用代理类的任何方法,此方法都会执行
-
Object proxy:代理对象自身的引用。
-
Method method:当前被调用的方法。
-
Object[] args:当前被调用方法用到的参数
代码实现:
Rent (租房)即代理实现的接口
public interface Rent {
public void rent();
}
Host (房东)即真实角色
//真实角色: 房东
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子");
}
}
ProxyInvocationHandler 即处理生成代理和调用方法
//动态代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
//注入
public void setTarget(Object target) {
this.target = target;
}
//生成动态代理类
public Object getProxy() {
return Proxy.newProxyInstance(
this.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
//处理动态代理是实例,并返回一个结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
Client:租客
//租客
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host); //将真实角色放置进去!
Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
proxy.rent();
}
}
//结果输出:房东要出租房子
四、AOP
AOP是面向切面编程, 例如转账功能,在转账代码的前后需要一些非业务方面的处理 记录日志,这些代码就可以使用AOP将其切入到转账代码的前后, AOP的优点就是降低代码之间的耦合,提高代码的复用性。 (切入点、切面、通知)
代码实现:
使用自定义类的方式来实现AOP,开发中建议使用
环境准备:新建一个UserService接口,里面定义增删改查四个方法,创建它的实现类实现它的方法UserServiceImpl,
【重点】使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
DiyPointcut新建自定义切面类
//自定义类实现aop的方式
public class DiyPointcut {
public void before(){
System.out.println("------方法执行前----------");
}
public void after(){
System.out.println("------方法执行后----------");
}
}
applicationContext.xml,注意要有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"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注册bean-->
<bean id="userService" class="com.kuang.UserserviceImpl"/>
<bean id="diy" class="com.kuang.aop2.DiyPointcut"/>
<aop:config>
<!--自定义切面,ref要引用类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="diyPonitcut"expression="execution(*com.kuang.UserserviceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="diyPonitcut"/>
<aop:after method="after" pointcut-ref="diyPonitcut"/>
</aop:aspect>
</aop:config>
</beans>
五、spring的事务管理
事务的四个特性ACID原则(原子性,一致性,隔离性,持久性)
业务的增删改都需要事务,事务处理都在service层
1.要添加aop织入的包
<!--aop织入-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
2.spring-service.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描service相关的bean -->
<context:component-scan base-package="com.kuang.service" />
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
使用注解配置事务
此时只需要在配置文件中添加下面内容:
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启注解事务驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
然后使用@Transactional 注解即可,该注解可以用于类上,也可以用于方法上,需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。
添加在类名上:
@Transactional
public class UserServiceImpl implements UserService {}
用在方法上,表示当抛出空指针异常时会进行回滚:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = NullPointerException.class)
public void addUser(User user) throws Exception {
userDao.addUser(user);
}
如果配置了事务,测试时获取bean时,返回的对象一定是接口
原因:
对于Spring AOP 采用两种代理方法,一种是常规JDK,一种是CGLIB,代理对象实现了至少一个接口,默认使用JDK动态创建代理对象,当代理对象没有实现任何接口时,就会使用CGLIB方法 ,就会报错
添加在类名上:
@Test
public void test22() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//接口
UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
List<User> users = userService.selectUser();
for (User user : users) {
System.out.println(user);
}
}
六、 @Autowired 和 @Resource 的区别
两个都是用作bean的注入
共同点:
@Resource和@Autowired都可以作为注入属性的修饰 ,在接口仅有单一实现类的时候,两个的注解修饰效果是相同的。
不同点:
- @Resource是Java自己的注解 ,@Autowired是 spring中的注解
- @Resource有两个属性是比较重要的,分是name和type , name属性解析为bean的名字 , 而type属性则解析为bean的类型 。如果都不指定,这是通过反射机制默认是按照 byName自动注入策略
- @Autowired 是spring2.5版本引入的 , Autowired只根据type进行注入,不会去匹配name。 如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier (就是一个接口有两个实现类无法辨别)
例子:
创建一个UserDao接口
public interface UserDao {
public void getUser();
}
UserDao接口,有两个实现类
创建UserDaoImplMysql类实现UserDao,并在类上添加注解注入到spirng中
@Repository
public class UserDaoImplMysql implements UserDao {
public void getUser() {
System.out.println("MySQL获取用户数据!");
}
}
UserDaoImplOracle
@Repository
public class UserDaoImplOracle implements UserDao {
public void getUser() {
System.out.println("Oracle获取用户数据!");
}
}
创建UserService接口
public interface UserService {
public void getUser();
}
创建UserServiceImpl类实现UserService
@Service
public class UserServiceImpl implements UserService {
//使用@Resource
//@Resource(name = "userDaoImplOracle") //按bena的名字
@Resource(type = UserDaoImplOracle.class) //按类型
//使用@Autowired
@Autowired
@Qualifier("userDaoImplOracle") //要指定是哪个bean
private UserDao userDao;
public void getUser() {
userDao.getUser();
}
}
测试类:
@Test
public void test001(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
userService.getUser();
}