一、Spring框架搭建与使用
1. 官网上下载 jar 包,就是速度有些慢。 2. 导包:只需要导入五个 spring-frameword 中的 jar 包和一个依赖包,分别是
spring-aop-5.1.2.RELEASE.jar
spring-beans-5.1.2.RELEASE.jar
spring-context-5.1.2.RELEASE.jar
spring-core-5.1.2.RELEASE.jar
spring-expression-5.1.2.RELEASE.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
3. 创建 Spring 框架的主配置文件,一般命名为 applicationContext.xml,一般放置在 src 目录下,如果使用的是 IDEA 开发环境,则可以直接创建出一个 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">
</beans>
4. 配置 bean ,这一步骤是为了配置 Spring 框架的 IOC 和 DI,在配置文件中通过 bean 标签配置每一个类,配置完后 Spring 框架会自动创建类的对象,并存放在 Spring 容器中。
<bean name="user" class="com.gec.domain.User"></bean>
5. 在测试方法中:
使用如下方法加载 Spring 配置文件,获取到 Spring 容器。
ApplicationContext ac = new ClassPathXMLApplicationContext("applicationContext.xml");
6. 从 Spring 容器中获取到对象:
通过如下方法获取到指定对象,例如
User user = ac.getBean("user", User.class);
二、IOC(控制反转)
IOC 是 Spring 框架中非常重要的概念之一,它想表达的意思是,将对象创建的控制权交给 Spring 框架管理,Spring 会自动为我们创建对象,并把对象放置在 Spring 容器中,当我们需要创建对象时,可直接到容器中获取到对象。
- XML 方式实现 IOC
- 在 Spring 配置文件中使用 bean 标签配置每一个类;
- 在测试方法中读取配置文件,Spring 会自动为配置的类创建对象并存储到 Spring 容器中
总结:需要为每一个类都使用 bean 标签配置一次,过于麻烦。
<bean name="user" class="com.gec.domain.User" scope="prototype" factory-method="createUser">
</bean>
- bean 标签中常用的属性:
id:给 bean 设置唯一标识
name:给 bean 设置名称;
class:类的全路径名称;
scope:设置单例还是多例,若值为 singleton 则表示单例;若值为 prototype 则表示多例。
factory-bean:选择创建对象时使用的 bean 对象,一般配合 factory-method 一起使用,表示使用指定 bean 中的方法创建对象。
factory-method:选择创建对象时调用的方法,若没有设置该属性,则默认是调用无参构造方法创建对象。
- 注解方式实现 IOC
- 在类上添加注解 @Component ,相当于是配置 bean 。另外,由 @Component 衍生的三个作用相同的注解,分别是 @Controller、@Service、@Repository,主要是用在不同包下时加以区分。还可以像 XML 方式那样指定单例还是多例。
@Component
@Scope(scopeName = "prototype")
public class User {
...
}
- 在 Spring 配置文件中配置注解扫描。仅仅在类上添加注解还不够,还得通知 Spring 框架使用了注解配置,使用标签 context:component-scan 开启扫描,扫描指定包下的注解。另外需要注意的是,开启注解的标签需要配置了头部信息才能使用,需要添加 context 信息。
<?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.gec"/>
</beans>
三、DI(依赖注入)
DI 属于 IOC 内的内容,在实现 DI 前必须先实现 IOC。
DI 表达的意思是,在对象创建的过程中,将对象依赖的属性通过配置设置值给该对象。
- XML 方式实现 DI:在 bean 标签中配置。
可以有四种注入方式,分别为 set方法注入、构造方法注入、p命名空间注入和 spel表达式注入,主要使用前两种方式。
- set 方法注入:通过子标签 property 给类的属性赋值,类必须有 set 方法。
<bean name="user" class="com.gec.domain.User">
<property name="username" value="张三"/>
</bean>
- 构造方法注入:在类中重载构造方法,通过子标签 constructor-arg 给构造方法的参数传值。
<bean name="user" class="com.gec.domain.User">
<!-- 注入方式:构造方法注入
1. 使用标签 constructor-arg 添加构造函数参数
2. index 属性设置参数的先后顺序,在有多个构造方法时可以使用
3. type 属性设置参数的类型,在有相同参数名但不同类型时 可以使用该属性加一区分
-->
<constructor-arg name="username" value="1" index="2"/>
<constructor-arg name="password" value="1234" type="int" index="0"/>
<constructor-arg name="girl" ref="girl" index="1"/>
</bean>
- p 命名空间方式注入:首先要在xml 头部添加 p 标签信息,然后才能在 property 标签中以 p:属性名 = “属性值” 方式注入值。
<bean name="user" class="com.gec.domain.User" p:username="p命名空间" p:password="12" p:girl-ref="girl">
</bean>
- spel 表达式注入:在 property 标签的 value 属性中,使用 ‘#’ 添加字符串类型的值,其他基本数据类型或引用类型则与 set 方式一样。
<bean name="user" class="com.gec.domain.User">
<property name="username" value="#{'宋江'}"/>
</bean>
- 注解方式实现 DI:在使用了注解 @Component 的类中
- @Value :若是基本数据类型 + String 类型,则可以使用该注解注入值;
- @Autowired :引用数据类型使用该注解注入,前提是该引用类型也配置了 bean。
- @Qualifier :当有多个相同类的对象时,可使用该注解加以区分。
- @Resource :是 Java 的注解,它的作用相当于是 @Autowired + @Qualifier,相当于简写版。
@Component
@Scope(scopeName = "prototype")
public class User {
@Value("王五")
private String username;
@Autowired
private Girl girl;
...
}
- 在 XML 中注入复杂数据类型(数组、List、Map):在 property 标签内配置,不建议直接使用属性 value 设置值,应使用相应的子标签注入。
- 数组:使用 array 标签,在子标签 value 中注入基本数据类型或 String 类型,在子标签 ref 中使用属性 bean 注入引用类型。
<property name="array">
<array>
<value>12</value>
<ref bean="girl"></ref>
</array>
</property>
- List 集合 :使用 List 标签,在子标签 value 中注入基本数据类型或 String 类型,在子标签 ref 中使用属性 bean 注入引用类型。
<property name="list">
<list>
<value>12</value>
<ref bean="girl"></ref>
</list>
</property>
- map 集合 :使用 map 标签,子标签 entry 设置每一个键值对,属性 key、value 对应键值对。
<property name="map">
<map>
<entry key="name" value="大湖"/>
<entry key="mygirl" value-ref="girl"/>
</map>
</property>
- properties:…
四、AOP(面向切面编程)
- AOP 采用横向抽取机制,将涉及多业务流程的通用功能抽取出来并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
- AOP 的经典应用中就有:事务管理。
- AOP 中的专业术语:
- Target(目标类):需要被代理的类;
- joinPoint(连接点):可能被 Spring 拦截的点(方法);
- pointCut(切入点):已经被增强的连接点(方法);
- advice(通知/增强):增强的代码,通用功能;
- weaving(织入):是指把增强的 advice 应用到目标对象 target 来创建代理类 proxy 的过程;
- AspectJ(切面):切入点 pointCut 和 通知 Advice 的结合。
- proxy(代理类).
- XML 方式实现 AOP 的事务管理:
- 导包:除了 4 个基本的 spring-frameword 的jar 包和 logging 包外,还需要4 个jar包,分别是
spring-aop-5.2.0.RELEASE.jar
spring-aspects-5.2.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar - 在 Spring 配置文件的头部信息中,添加 aop 的信息。
- 准备目标类和通知类,并在 xml 中配置 bean 信息。
- 在 xml 中使用 aop:config 标签配置 AOP 信息。
<aop:config>
<!-- execution:执行方法,表达式简化
1. 表达式中的访问修饰符可以去掉。
2. 返回值类型可以为多种类型,设置为 *
3. 执行方法名以及所属类名的前缀都可以是多样的。
4. 访问当前包以及子包 ".."
-->
<aop:pointcut id="pc" expression="execution(* com.gec.service..*Service.*User(..))"/>
<aop:aspect ref="myAdvice">
<!-- <aop:before method="before" pointcut-ref="pc"></aop:before>-->
<!-- <aop:after method="after" pointcut-ref="pc"></aop:after>-->
<!-- <aop:after-returning method="after_returning" pointcut-ref="pc"></aop:after-returning>-->
<!-- <aop:after-throwing method="after_throwing" pointcut-ref="pc"></aop:after-throwing>-->
<aop:around method="around" pointcut-ref="pc"></aop:around>
</aop:aspect>
</aop:config>
- 五种事务通知:
before:前置通知,在方法执行前被调用;
after-returning:后置通知,若执行方法没有抛出异常,则在方法执行后被调用,否则,抛出异常则不调用;
after:后置通知(最终通知):不管执行方法是否抛出异常,都会在方法执行后被调用;
after-throwing:异常通知,在方法抛出异常时被调用,可在该异常通知方法中设置异常处理;
around:环绕通知,相当于 before 和 after 的结合体,该环绕通知方法要设置参数 ProceedingJoinPont,用于执行完前置通知内容后调用执行方法,并接收执行方法的返回值,在执行完后置通知内容后,再将返回值返回给上层调用,所以该环绕通知方法还需要将返回值类型设置为 Object 类型。
- 注解方式实现 AOP 的事务管理:
- 使用注解的方式还需要在 xml 的头部信息中添加 context 信息。使用标签 context:component-scan 扫描指定包下的注解信息。
- 配置 AOP 注解扫描,通知 Spring 框架 AOP 事务管理使用了注解的方式。使用标签 aop:aspectj-autoproxy 配置。
<!-- 配置 AOP 注解扫描
此处开关必须打开
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 在目标类和通知类中都是用注解 @Component 将类交给 Spring 框架创建实例对象。
- 在通知类中还需要添加注解 @Aspect 表示该类是一个切面,另外在相应的通知方法中,使用相应的注解 @Before/@AfterReturning/@After/@AfterThrowing/@Around 设置方法为相应的事务通知方法。
@Component(value = "myAdvice")
@Aspect
public class MyAdvice {
@Before(value = "execution(* com.gec.service..*Service.*User())")
void before(){
System.out.println("开启事务");
}
@After(value = "execution(* com.gec.service..*Service.*User())")
void after(){
System.out.println("提交事务");
}
@AfterReturning(value = "execution(* com.gec.service..*Service.*User())")
void after_returning(){
System.out.println("后置通知,出现异常则不执行次方法");
}
@AfterThrowing(value = "execution(* com.gec.service..*Service.*User())")
void after_throwing(){
System.out.println("异常处理通知");
}
@Around(value = "execution(* com.gec.service..*Service.*User())")
Object around(ProceedingJoinPoint pj) throws Throwable {
//执行前置通知
System.out.println("前置通知");
//调用执行方法,获得执行方法的返回值
Object proceed = pj.proceed();
//执行后置通知
System.out.println("后置通知");
//后置通知结束后 需要把返回值返回给上层调用
return proceed;
}
}
五、JdbcTemplate 模板类
- JdbcTemplate 模板类是 Spring 为传统的 JDBC API 进行封装,简化持久层操作。
- 使用 JdbcTemplate:
- 导包:除了四个基本的spring-frameword的jar包和logging包外,还需要
spring-aop-5.2.0.RELEASE.jar
spring-jdbc-5.2.0.RELEASE.jar
spring-test-5.2.0.RELEASE.jar
spring-tx-5.2.0.RELEASE.jar
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
mysql-connector-java-8.0.11.jar - 在 xml 中配置数据源。这里使用的是 C3P0 方式连接数据库,使用 bean 标签配置 ComboPooledDataSource 数据源,设置连接数据库的信息。
<!-- 配置数据源-->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="1234"/>
</bean>
- 在 xml 中配置 JdbcTemplate,将数据源注入到 JdbcTemplate 中,让 Spring 框架自动为我们创建 JdbcTemplate 对象。
<!-- 配置 JDBCTemplate-->
<bean name="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
- 在测试类中使用 JdbcTemplate 对象进行增删改查:
增删改使用 update(String sql[,Object…arg])方法即可;
查询可分为三种情况:
a. 返回 List 集合,使用 query(String sql,RowMapper< T > rowMapper)方法,匿名实现RowMapper 接口,设置查询结果集的接收类型。
b. 返回一个 Object 对象,使用 queryForObject(String sql,RowMapper< T > rowMapper,Object…arg),匿名实现 RowMapper 接口,设置查询结果集的接收类型,可变参数设置查询的条件。
c. 返回一个整数,如计算总数等情况,使用 queryForObject(String sql,Class< T > requiredType),requiredType 设置返回类型,如可设置为 Integer.class 类型。
@Test
public void fun(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("JdbcTemplate");
/*
增删改使用 update方法
*/
int i = template.update("insert into province values(null, '山东')");
System.out.println(i);
/*、
查询使用 query、queryForObject等方法
*/
//返回 list 集合
List<Province> query = template.query("select * from province", new RowMapper<Province>() {
@Override
public Province mapRow(ResultSet resultSet, int i) throws SQLException {
Province p = new Province();
p.setPid(resultSet.getInt(1));
p.setPname(resultSet.getString(2));
return p;
}
});
for (Province province : query) {
System.out.println(province);
}
System.out.println("------------------------");
//返回一个 Object 对象
Province province = template.queryForObject("select * from province where pid = ?", new RowMapper<Province>() {
@Override
public Province mapRow(ResultSet resultSet, int i) throws SQLException {
Province p = new Province();
p.setPid(resultSet.getInt(1));
p.setPname(resultSet.getString(2));
return p;
}
}, 1);
System.out.println(province);
System.out.println("------------------------");
//返回整型数据
Integer count = template.queryForObject("select count(*) from province", Integer.class);
System.out.println("总记录数:" + count);
}