Spring
概述
- Spring是一个开源框架,是为了解决企业级应用开发的复杂性而设计的
- Spring最根本的使命:全方位简化Java开发
- 核心思想:
- ioC:控制反转,就是把依赖对象创建的控制权交给第三方来处理
- DI:依赖注入,组件之间的依赖关系在程序运行期间由第三方动态地注入依赖
使用
添加Spring依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.22</version> </dependency>
添加Spring容器的配置类,并添加组件
//作为Spring容器的配置类,告诉Spring容器中管理哪些组件 @Configuration public class AppConfig { //组件 @Bean public EmpDao empDao(){ return new OracleEmpDaoImpl();//返回接口的实现类 } }
测试(创建一个Spring容器,并得到组件)
//从Spring的容器中得到一个组件(EmpDao) @Test public void getEmpDao(){ //得到Spring容器 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); //得到容器中的组件 EmpDao contextBean = context.getBean(EmpDao.class); contextBean.insert(); }
- ApplicationContext:是一个Spring接口,负责管理应用的各个方面,如Bean的生命周期
- AnnotationConfigApplicationContext:是ApplicationContext接口的一个实现,用于从一个带有@Configuration注解的Java类来加载Spring应用上下文
得到组件
根据类型得到
EmpDao empDao = c.getBean(EmpDao.class);
根据名称得到
EmpDao empDao = c.getBean("oracleEmpDao") //配置类中 @Bean(name = "oracleEmpDao")//组件默认为方法名 public EmpDao empDao(){ return new OracleEmpDaoImpl();//返回接口的实现类 }
组件装配
@Bean public EmpService empService(){ EmpServiceImpl empService = new EmpServiceImpl(); empService.setEmpDao(empDao());//DI:依赖注入,属性注入 return empService; }
构造注入:通过构造参数,注入依赖
@Bean public EmpService empService(){ EmpServiceImpl empService = new EmpServiceImpl(mySqlEmpDao()); return empService; }
依赖注入的三种方式
- 属性注入
- 构造注入
- 接口注入(Spring不支持)
Bean说明
@Scope:作用域(单例:singleton(默认的),prototype(原生的))
@Bean @Scope("prototype") public EmpService empService(){ EmpServiceImpl empService = new EmpServiceImpl(); empService.setEmpDao(empDao()); return empService; } //单例指的是每一次调用只给同一个对象 //原生多例指的是每次调用都新创建一个对象
@Lazy:懒加载(延时加载,在getBean()的时候再加载),默认值@Lazy(false),容器启动的时候创建组件
注意:在@Scope为prototype的时候,一定是懒加载的,在组件单例的情况下设置@Lazy才有意义的
@Bean @Lazy(true)//懒加载 public EmpService empService(){ EmpServiceImpl empService = new EmpServiceImpl(); empService.setEmpDao(empDao()); return empService; }
组件扫描
@ComponentScan(basePackages = {"com.neu.dao","com.neu.service"}) public class AppConfig {}
@Component//组件名默认是类名首字母小写 public class OracleEmpDaoImpl implements EmpDao { @Override public int insert() { System.out.println("执行了Oracle的Emp添加操作"); return 0; } } @Component("oracleEmpDao")//参数是组件的别名 public class OracleEmpDaoImpl implements EmpDao { @Override public int insert() { System.out.println("执行了Oracle的Emp添加操作"); return 0; } }
- Component:用于定义一个Bean,Spring容器将负责这个Bean的生命周期,value属性允许你为这个Bean设置一个名称
自动装配
@Autowired//自动装配 public void setEmpDao(EmpDao empDao){ this.empDao = empDao; }
- @Autowired注解用于实现依赖注入,也就是自动装配,意味着Spring容器会自动解析并注入所需要的依赖,默认@Autowired按照类型来进行自动装配,可以将注解加到构造函数上,setter方法或者字段上,不推荐在字段上添加该注解,因为破坏了封装性
解决自动装配冲突(使用组件名装配)
@Component("EmpService") public class EmpServiceImpl implements EmpService { private EmpDao empDao; @Autowired//自动装配 @Qualifier("mySQLEmpDap")//使用组件名装配 public void setEmpDao(EmpDao empDao){ this.empDao = empDao; } @Override public int insert() { empDao.insert(); return 0; } }
- @Qualifier:用于消除自动装配的歧义
使用Spring的测试模块
添加依赖
<spring.version>5.3.22</spring.version> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency>
测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = AppConfig.class) public class EmpDaoTest2 { private EmpDao empDao; @Autowired() @Qualifier("mySQLEmpDap") public void setEmpDao(EmpDao empDao){ this.empDao = empDao; } @Test public void insert(){ empDao.insert(); } }
- @RunWith:是JUnit的一个注解,用于定义自定义的测试运行器
SpringJUnit4ClassRunner.class
是Spring提供的一个测试运行器,它拓展了JUnit的功能,使得测试类能够在Spring的应用上下文环境下执行(使得当前测试类变为Spring容器的组件)
- @ContextConfiguration:用于定义如何引导Spring应用程序上下文(Spring容器)
- @RunWith:是JUnit的一个注解,用于定义自定义的测试运行器
AOP
定义:面向切面编程,模块化横切关注点(把相同的代码加入到关注的方法上)
导入包
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.7.3</version> </dependency>
定义一个类,实现切面的功能
@Configuration @Component @Aspect//切面 @EnableAspectJAutoProxy//开启切面自动的代理 public class AOPUtil { //切入点,加入相同代码的一组关注点(连接点) @Pointcut("execution(* com.neu.dao.*.*(..))") public void pointcut(){ } //通知:加入的代码 @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.nanoTime(); //调用代理的方法 Object proceed = joinPoint.proceed(); long endTime = System.nanoTime(); System.out.println("insert方法的执行时间是:"+(endTime-startTime)+"纳秒"); return proceed; } }
- 连接点(joinPoint):程序执行过程中特定点(Spring中的方法)
- 通知(Advice):在特定连接点加入的代码
- 切入点(pointCut):加入相同通知的一组连接点
- 切面(Aspect):通知与切入点的组合
前置通知
@Before("pointcut()") public void before(JoinPoint joinPoint){ String signature = joinPoint.getSignature().getName(); String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName(); System.out.println(declaringTypeName+"中"+signature+"方法开始执行时间是:"+new Date()); }
返回后通知:执行了return语句后执行的代码
@AfterReturning("pointcut()") public void afterReturning(JoinPoint joinPoint){ String signature = joinPoint.getSignature().getName(); String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName(); System.out.println(declaringTypeName+"中"+signature+"方法结束时间是:"+new Date()); }
异常通知
@AfterThrowing("pointcut()") public void afterThrowing(JoinPoint joinPoint){ String signature = joinPoint.getSignature().getName(); String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName(); System.out.println(declaringTypeName+"中"+signature+"方法异常发生时间是:"+new Date()); }
最终通知
@After("pointcut()") public void after(JoinPoint joinPoint){ String signature = joinPoint.getSignature().getName(); String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName(); System.out.println(declaringTypeName+"中"+signature+"方法最终结束时间是:"+new Date()); }
环绕通知
@Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.nanoTime(); //调用代理的方法 Object proceed = joinPoint.proceed(); long endTime = System.nanoTime(); System.out.println("insert方法的执行时间是:"+(endTime-startTime)+"纳秒"); return proceed; }
AOP用途:日志、权限管理、事务
Spring对数据源的支持
导入连接池及数据库驱动
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>
定义一个属性文件(db.properties),在其中配置数据库连接池需要的参数
jdbc.username=root jdbc.password=root jdbc.url=jdbc:mysql://localhost:3306/neusoft?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.driverClassName=com.mysql.cj.jdbc.Driver jdbc.maxActive=20 jdbc.maxWait=2000 jdbc.maxIdle=5
定义一个配置类,读取数据库连接属性文件
@Configuration @PropertySource("classpath:db.properties") public class DBConfig { @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Value("${jdbc.url}") private String url; @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.maxActive}") private Integer maxActive; @Value("${jdbc.maxWait}") private Integer maxWait; @Value("${jdbc.maxIdle}") private Integer maxIdle; @Bean public DataSource dataSource(){ BasicDataSource bs = new BasicDataSource(); bs.setUsername(username); bs.setPassword(password); bs.setUrl(url); bs.setDriverClassName(driverClassName); bs.setMaxActive(maxActive); bs.setMaxWait(maxWait); bs.setMaxIdle(maxIdle); return bs; } }
测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {AppConfig.class, DBConfig.class}) public class EmpDaoTest2 { private DataSource dataSource; @Autowired public void setDataSource(DataSource dataSource){ this.dataSource = dataSource; } @Test public void testDataSource() throws SQLException { Connection connection = dataSource.getConnection(); connection.close(); } }
Spring与MyBatis的集成
导入依赖jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.8.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>
配置SqlSessionFactory
@Configuration @MapperScan("com.neu.mapper") public class MyBatisConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); //设置数据源 sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean.getObject(); } }
创建Mapper接口和对应的xml文件
测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {DBConfig.class, MyBatisConfig.class}) public class DpetMapperTest { private DeptMapper deptMapper; @Autowired public void setDeptMapper(DeptMapper deptMapper){ this.deptMapper = deptMapper; } @Test public void testGetById(){ Dept dept = deptMapper.selectByPrimaryKey(10L); System.out.println(dept); } }