Spring
Spring开发步骤
- 导入Spring开发的基本包坐标
- 编写Dao接口和实现类
- 创建Spring核心配置文件
- 在Spring配置文件中配置UserDaoImpl
- 使用Spring的API获得Bean实例
Spring配置文件
-
Bean标签范围配置
scope:指对象的作用范围
-
singleton:默认值,单例的
实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
-
prototype:多例的
实例化时机:当调用getBean( )方法时实例化Bean
-
-
Bean生命周期配置
- init-method:指定类中的初始化方法名称
- destroy-method:指定类中销毁方法名称
-
Bean实例化三种方式
- 无参构造方法实例化
- 工厂静态方法实例化
- 工厂实例方法实例化
-
依赖注入
<!--set方法注入 常用 涉及的标签:property 出现的位置:bean标签的内部 name:用于注入时所调用的set方法名称 value:用于提供基本类型和string类型数据 ref:用于指定其他的bean类型数据,它指的就是在spring的Ioc核心容器中出现过的bean对象 优势:创建对象时没有明确的限制,可以直接使用默认构造函数 弊端:如果由某个成员,必须有值,则获取对象时,有可能set方法没有执行 -->
<!--
复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签
list array set
用于给List结构集合注入的标签
map props
结构相同,标签可以互换
-->
例:
package com.ego.dao.impl;
import com.ego.dao.UserDao;
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save running!");
}
}
package com.ego.service.impl;
import com.ego.dao.UserDao;
import com.ego.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
package com.ego.demo;
import com.ego.dao.UserDao;
import com.ego.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserDaoDemo {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.ego.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.ego.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
Spring配置数据源
数据源(连接池)的作用
- 提高程序性能
- 事先实例化数据源,初始化部分连接资源
- 使用连接资源时从数据源中获取
- 使用完毕后将连接资源归还给数据源
常见的数据源(连接池):DBCP,C3P0,BoneCP,Druid
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import org.junit.jupiter.api.Test;
import java.sql.SQLException;
public class DataSourceTest {
@Test
public void test1() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/travel");
dataSource.setUsername("root");
dataSource.setPassword("root");
DruidPooledConnection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
}
抽取jdbc.properties文件
jdbc.Driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/travel
jdbc.Username=root
jdbc.password=root
@Test
public void test2() throws SQLException {
//读取配置文件
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
String driver = rb.getString("jdbc.Driver");
String url = rb.getString("jdbc.url");
String username = rb.getString("jdbc.Username");
String password = rb.getString("jdbc.password");
//创建数据源
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/travel"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
@Test
public void test3() throws SQLException {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) app.getBean("dataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
Spring加载properties文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!--加载外部的配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.Driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.Username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
Spring注解开发
Spring原始注解
@Component
使用在类上用于实例化Bean
@Controller
使用在web层类上用于实例化Bean
@Service
使用在service层类上用于实例化Bean
@Repository
使用在dao层类上用于实例化Bean
@Autowired
使用在字段上用于根据类型依赖注入
@Qualifier
结合@Autowired一起使用用于根据名称进行依赖注入
@Resource
相当于@Autowired+@Qualifier,按照名称进行注入
@Value
注入普通属性
@Scope
标注Bean的作用范围
@PostConstruct
使用在方法上标注该方法是Bean的初始化方法
@PreDestroy
使用在方法上标注该方法是Bean的销毁方法
注意:使用注解开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
package com.ego.dao.impl;
import com.ego.dao.UserDao;
import org.springframework.stereotype.Component;
//<bean id="userDao" class="com.ego.dao.impl.UserDaoImpl"></bean>
@Component("userDao")
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save running!");
}
}
package com.ego.service.impl;
import com.ego.dao.UserDao;
import com.ego.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
//<bean id="userService" class="com.ego.service.impl.UserServiceImpl">
@Component("userService")
public class UserServiceImpl implements UserService {
private UserDao userDao;
//<property name="userDao" ref="userDao"></property>
@Autowired
@Qualifier("userDao")
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
package com.ego.demo;
import com.ego.dao.UserDao;
import com.ego.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserDaoDemo {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
<?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">
<context:component-scan base-package="com.ego"></context:component-scan>
</beans>
Spring新注解
使用原始注解不能全部代替xml配置文件,还需要使用注解替代的配置如下:
- 非自定义的Bean的配置:
- 加载properties文件的配置:<context:property-placeholder >
- 组件扫描的配置:context:component-scan
- 引入其他文件:
@Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写,这时系统将直接扫描该配置类。
@ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value:和basePackages的作用一样的,用于指定创建容器时要扫描的包
使用此注解等于在xml中配置了:<context:component-scan base-package=“com”></context:component-scan>
@Bean注解
作用:把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:
name:用于指定bean的id。默认值是当前方法的名称
细节:当使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。查找的方式和@Autowired注解的方式一样
@Import注解
作用:导入其他的配置类。
属性:
value:指定其他配置类的字节码。
使用方法:@Import(xxxx.class) //xxxx为导入的配置类名
使用了Import注解后,有Import注解的为父配置类,而导入的都是子配置类。
@PropertySource
作用:导入properties文件
属性:
value:指定文件的名称和路径。关键字:classpath:表示类路径下
使用方法:@PropertySource(“classpath:jdbcConfig.properties”)
Spring集成Junit
步骤:
- 导入spring集成Junit的坐标
- 使用@Runwith注解替换原来的运行期
- 使用@ContextConfiguration指定配置文件或配置类
- 使用@Autowired注入需要测试的对象
- 创建测试方法进行测试
//目标:测试一下spring的bean的某些功能
@RunWith(SpringJUnit4ClassRunner.class)//junit整合spring的测试//立马开启了spring的注解
@ContextConfiguration(locations="classpath:applicationContext.xml")//加载核心配置文件,自动构建spring容器
public class SpringTest {
//使用注解注入要测试的bean
@Autowired
private HelloService helloService;
@Test
public void testSayHello(){
//获取spring容器
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//从spring容器中获取bean对象
// HelloService helloService=(HelloService)applicationContext.getBean("helloService");
//测试业务功能
helloService.sayHello();
}
}
AOP
概念
AOP是面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
底层实现:基于JDK的动态代理和基于Cglib的动态代理
作用及其优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
相关概念
- Target(目标对象):代理的目标对象
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice(通知/增强):所谓通知是指拦截到Joinpoint之后要做的事情
- Aspect(切面):是切入点和通知(引介)的结合
- Weaving(织入):只是把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入
AOP开发明确的事项
-
需要编写的内容
- 编写核心业务代码(目标类的目标方法)
- 编写切面类,切面类中有通知(增强功能方法)
- 在配置文件中,配置织入关系,即将哪些通知和哪些连接点进行结合
-
AOP技术实现的内容
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
-
AOP底层使用哪些代理方式
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式
基于XML的AOP开发
快速入门
- 导入AOP相关坐标
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面类的对象创建权交给spring
- 在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: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="target" class="com.ego.aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="com.ego.aop.MyAspect"></bean>
<!--配置织入:告诉spring框架 哪些方法需要增强-->
<aop:config>
<!-- 声明切面-->
<aop:aspect ref="myAspect">
<!-- 切面:切点+通知-->
<aop:before method="before" pointcut="execution(public void com.ego.aop.Target.save())"/>
</aop:aspect>
</aop:config>
</beans>
切点表达式的写法
execution([修饰符]返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号*代表任意
- 包名和类名之间一个点.代表当前包下的类,两个点…表示当前包及其包下的子类
- 参数列表可以使用两个点…表示任意个数,任意类型的参数列表
通知的类型
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式”></aop:通知类型 >
(1)前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
(2)返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
(4)后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
(5)环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。
基于注解的AOP开发
快速入门
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部增强方法)
- 将目标类和切面类的对象创建权交给spring
- 在切面类中使用注解配置织入关系
- 在配置文件中开启组件扫描和AOP的自动代理
- 测试
注解
@Component 将切面类交给容器管理
@Aspect 声明为切面类
@Around(value="")环绕通知
@Before(value="")前置通知
@AfterReturning(value="")返回成功通知
@AfterThrowing(value=" “)返回异常通知
@After(value=”")最终通知
@Pointcut("")定义切入点表达式
JdbcTemplate
开发步骤
- 导入spring-jdbc和spring-tx坐标
- 创建数据库表和实体
- 创建JdbcTemplate对象
- 执行数据库操作
package com.ego.test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import java.beans.PropertyVetoException;
public class JdbcTemplateTest {
@Test
public void test() throws PropertyVetoException {
//创建数据源对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("root");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
int row = jdbcTemplate.update("insert into account values (?,?)", "ego", 5000);
System.out.println(row);
}
}
使用spring
package com.ego.test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.beans.PropertyVetoException;
public class JdbcTemplateTest {
@Test
public void test() throws PropertyVetoException {
// //创建数据源对象
// ComboPooledDataSource dataSource = new ComboPooledDataSource();
// dataSource.setDriverClass("com.mysql.jdbc.Driver");
// dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
// dataSource.setUser("root");
// dataSource.setPassword("root");
//
// JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);
int row = jdbcTemplate.update("insert into account values (?,?)", "lisa", 10000);
System.out.println(row);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
">
<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"></property>
</bean>
<!--数据源对象-->
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}"></property>
<property name="jdbcUrl" value="${url}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
</beans>
CRUD操作
package com.ego.test;
import com.ego.domian.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testUpdate(){
jdbcTemplate.update("update account set money = ? where name = ?", 2000, "tom");
}
@Test
public void testDelete(){
jdbcTemplate.update("delete from account where name = ?", "lisa");
}
@Test
public void testQueryAll(){
List<Account> list = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(list);
}
@Test
public void testQueryOne(){
Account ego = jdbcTemplate.queryForObject("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), "ego");
System.out.println(ego);
}
@Test
public void testQueryCount(){
Integer count = jdbcTemplate.queryForObject("select count(*) from account", Integer.class);
System.out.println(count);
}
}
Spring继承web环境
-
ApplicationContext应用上下文获取方式
应用上下文对象是通过new ClassPathXmlApplicationContext(Spring配置文件)方式获取的,但是每次从容器获得Bean时都要编写new ClassPathXmlApplicationContext(Spring配置文件),这样的弊端是配置文件加载多次,应用上下文对象创建多次
在web项目中,可以使用ServletContextListener监听web应用的启动,我们可以在web应用启动时,就加载Spring配置文件,创建上下文对象ApplicationContext,再将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。
-
Spring提供获取应用上下文的工具
Spring提供了一个监听器ContextLoaderListener,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象
所以我们需要做的只有两件事:
- 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
- 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext