1.依赖注入
依赖:就是一种关系,代表了软件实体之间的联系。以表明一个软件实体,依靠另一个软件实体的规范或实现,而不能自立或自给。
原本是这么写的,耦合度太高
包结构
dao包下
UserDao
package com.example.dao;
import com.example.entity.User;
public interface UserDao {
User selectUserById(Long id);
}
UserDaoImpl
package com.example.dao;
import com.example.entity.User;
public class UserDaoImpl implements UserDao {
@Override
public User selectUserById(Long id) {
System.out.println("UserDaoImpl.selectUserById");
return null;
}
}
service包下
UserService
package com.example.service;
import com.example.entity.User;
public interface UserService {
User findUserById(Long id);
}
UserServiceImpl
package com.example.service;
import com.example.dao.UserDao;
import com.example.entity.User;
public class UserServiceImpl implements UserService {
UserDao userDao = new UserDaoImpl();
@Override
public User findUserById(Long id) {
return userDao.selectUserById(id);
}
}
entity包
User
package com.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String password;
}
Test包下
UserServiceImplTest
package com.example.service;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceImplTest extends TestCase {
UserService userService = new UserServiceImpl();
@Test
public void testFindUserById() {
User user=userService.findUserById(1L);
System.out.println(user);
}
}
运行效果
纯xml方式
1.添加jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${Spring.version}</version>
</dependency>
2.创建配置文件applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--Spring主配置文件-->
<!--配置容器管理的Bean-->
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserServiceImpl">
<!--引用的名称,引用的ID-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>
applicationContext.xml文件中标签的属性不自动提示
创建两个Bean,userService中引用了userDao,所以多添加一个属性,表示引用关系
容器获取的Bean默认是单例模式,通过scope属性设置
Spring容器创建bean的使用调用的是类的空参构造
3.在使用时,如在Test中
(1)实例化容器对象
(2)从容器中获取Bean实例
package com.example.service;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceImplTest extends TestCase {
UserService userService ;
@Test
public void testFindUserById() {
//1.实例化容器对象
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中获取Bean实例
UserService userService=(UserService) ac.getBean("userService");
User user=userService.findUserById(1L);
System.out.println(user);
}
}
这里需要注意的是,这是使用set方式注入
所以要在实现类中添加set方法
package com.example.service;
import com.example.dao.UserDao;
import com.example.entity.User;
public class UserServiceImpl implements UserService {
UserDao userDao ;
// xml方式需要提供属性的set方法来绑定
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User findUserById(Long id) {
return userDao.selectUserById(id);
}
}
通过构造器的方式注入
XML+注解(annotation)
1.在相应的位置添加注解
1、@controller 控制器(注入服务) 2、@service 服务(注入dao) 3、@repository dao(实现dao访问) 4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)@Component,@Service,@Controller,@Repository注解的类,并把这些类纳入进spring容器中管理。 下面写这个是引入component的扫描组件 <context:component-scan base-package=”com.example”>
2.修改配置文件applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--开启注解扫描-->
<context:component-scan base-package=" com.example"/>
</beans>
可以扫描com.example下所有带注解的部分
3.修改测试调用
package com.example.service;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//使用spring-test测试,导入相应的包
//加载主配置文件
@ContextConfiguration("classpath:applicationContext.xml")
//使用spring的测试
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceImplTest extends TestCase {
//自动装配
@Autowired
UserService userService ;
@Test
public void testFindUserById() {
User user=userService.findUserById(1L);
System.out.println(user);
}
}
导入spring-test包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${Spring.version}</version>
</dependency>
已经导入Spring-context包但是没有出现注解
解决办法:清一下idea的缓存
java.lang.IllegalStateException: SpringJUnit4ClassRunner requires JUnit 4.12 or higher.
解决办法:
JUnit需要更高版本
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
运行结果:
纯注解方式
1.添加配置类AppConfig
package com.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.ContextConfiguration;
/*
* Spring 主配置类
* */
//表示该类是一个spring核心配置类
@ContextConfiguration
//表示扫描包
//注意:可以通过basepackage配置扫描路径,如果不写,默认扫描该类所在的包以及子包
@ComponentScan
public class AppConfig {
}
2.修改测试类参数
package com.example.service;
import com.example.AppConfig;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//使用spring-test测试,导入相应的包
@ContextConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceImplTest extends TestCase {
//自动装配
@Autowired
UserService userService ;
@Test
public void testFindUserById() {
User user=userService.findUserById(1L);
System.out.println(user);
}
}
运行结果:
添加一个list测试
AppConfig
package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.event.annotation.BeforeTestExecution;
import java.util.ArrayList;
import java.util.List;
/*
* Spring 主配置类
* */
//表示该类是一个spring核心配置类
@ContextConfiguration
//表示扫描包
//注意:可以通过basepackage配置扫描路径,如果不写,默认扫描该类所在的包以及子包
@ComponentScan
public class AppConfig {
@Bean
public List<String> list() {
List<String> list = new ArrayList<>();
list.add("AA");
list.add("BB");
list.add("CC");
return list;
}
}
测试类
package com.example.service;
import com.example.AppConfig;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
//使用spring-test测试,导入相应的包
@ContextConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceImplTest extends TestCase {
//自动装配
@Autowired
UserService userService ;
@Autowired
List<String> list;
@Test
public void testFindUserById() {
User user=userService.findUserById(1L);
System.out.println(user);
}
@Test
public void listTest(){
list.forEach(System.out::println);
}
}
运行结果
2.AOP面向切面编程
目的是为了更高效的代码复用
问题有很多方法的时候,实现类需要重复写非核心代码
Execution
XML方式
1.添加包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${Spring.version}</version>
</dependency>
2.创建通知类,共性逻辑代码
package com.example.advice;
import org.aspectj.lang.JoinPoint;
import java.util.Date;
/*
* Advice通知
* */
public class LogAdvice {
//jp对象是现场信息的封装
public void log(JoinPoint jp) {
//获取切入目标的方法名
String methodName = jp.getSignature().getName();
//获取切入目标的类名
String className = jp.getTarget().getClass().getName();
System.out.println(className+"类的"+methodName+"运行了,on:"+new Date());
}
}
package com.example.advice;
import org.aspectj.lang.JoinPoint;
import java.util.Date;
/*
* Advice通知
* */
public class PermissionAdvice {
public void check(JoinPoint jp) {
//获取切入目标的方法名
String methodName = jp.getSignature().getName();
//获取切入目标的类名
String className = jp.getTarget().getClass().getName();
System.out.println(className+"类的"+methodName+"运行了,on:"+new Date());
}
}
3.修改配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserServiceImpl">
<!--引用的名称,引用的ID-->
<property name="userDao" ref="userDao"/>
</bean>
<!--配置通知-->
<bean id="logAdvice" class="com.example.advice.LogAdvice"/>
<bean id="permissionAdvice" class="com.example.advice.PermissionAdvice"/>
<!--配置切面-->
<aop:config>
<aop:aspect ref="permissionAdvice">
<!--配置切点表达式execution-->
<aop:before method="check" pointcut="execution(* com.example..*ServiceImpl.find*(..))"/>
</aop:aspect>
</aop:config>
</beans>
特别注意execution表达式,非常灵活,指定哪些方法切入
上述代码达到的目的就是,在方法中没有写权限验证的代码但是,执行的时候依然会执行,就是因为AOP切到这个方法之前执行了
运行结果
XML+注解
主要是advice添加注解
1.对应位置添加注解
UserDaoImpl @Repository
UserServiceImpl @Service
LogAdvice
package com.example.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
* Advice通知
* */
@Component
//注解配置切面
@Aspect
public class LogAdvice {
//jp对象是现场信息的封装
@After("execution(* com.example..*ServiceImpl.find*(..))")
public void log(JoinPoint jp) {
//获取切入目标的方法名
String methodName = jp.getSignature().getName();
//获取切入目标的类名
String className = jp.getTarget().getClass().getName();
System.out.println(className+"类的"+methodName+"运行了,on:"+new Date());
System.out.println("日志记录");
}
}
PermissionAdvice
package com.example.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
* Advice通知
* */
@Component
@Aspect
public class PermissionAdvice {
@Before("execution(* com.example..*ServiceImpl.find*(..))")
public void check(JoinPoint jp) {
//获取切入目标的方法名
String methodName = jp.getSignature().getName();
//获取切入目标的类名
String className = jp.getTarget().getClass().getName();
System.out.println(className+"类的"+methodName+"运行了,on:"+new Date());
System.out.println("权限验证中...");
}
}
2.修改配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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
https://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--开启注解扫描-->
<context:component-scan base-package=" com.example"/>
<!--开启AOP-->
<aop:aspectj-autoproxy/>
</beans>
3.修改测试文件
package com.example.service;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceImplTest extends TestCase {
@Autowired
UserService userService;
@Test
public void testFindUserById() {
User user = userService.findById(1L);
System.out.println(user);
}
}
运行结果
纯注解方式
1.添加主配置类
package com.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.test.context.ContextConfiguration;
//spring主配置文件类
@ContextConfiguration
//扫描注解
@ComponentScan
//开启AOP
@EnableAspectJAutoProxy
public class AppConfig {
}
2.修改测试类
package com.example.service;
import com.example.AppConfig;
import com.example.entity.User;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(classes= AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceImplTest extends TestCase {
@Autowired
UserService userService;
@Test
public void testFindUserById() {
User user = userService.findById(1L);
System.out.println(user);
}
}
运行结果
3.声明式事务
事务四大特性:ACID
编程式事务:代码写死事务边界
声明式事务:动态确定事务边界
Propagation(传播行为)
被定义了声明式事务的方法,有权知道当前是否有事务开启,并有权决定是否加入当前事务。某个方法增加到当前事务,相当于事务被传播了。传播的实质,也就是对事务的边界,在程序运行期间动态地进行控制。
配置声明式事务管理器
spring由于需要为多种数据层的实现提供集成支持,针对不同的情况,定义了各种事务管理器,一定要选择正确的事务管理器
如:对应JDBC事务,对应Hibernate提供的事务,对应JTA事务
备注:大多数事务管理器需要设置dataSource属性或sessionFactory属性