【Spring实战】1.1 简化 Java 开发
依赖注入
什么是依赖注入
在构造函数中自行创建依赖的对象,高度耦合:
public DamselRescuingKnight() {
this.quest = new RescueDamselQuest();
}
将对象的依赖关系交给第三方组件管理,无需自行创建,这是依赖注入:
public BraveKnight(Quest quest) {
this.quest = quest;
}
Spring 怎么实现装配
怎么把 Quest 交给 Knight 呢?
XML 的装配方式:
<!-- 在pom.xml加上Spring的依赖,右键新建XML就可以看到Spring Config了 -->
<bean id="knight" class="sia.knights.BraveKnight">
<constructor-arg ref="quest"/>
</bean>
<bean id="quest" class="sia.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}"/>
</bean>
Java 的装配方式:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}
获取 Spring 装配好的 Bean
加载 Spring 的上下文
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("knights.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
阅读 spring core与context理解 后,我的理解是 Spring Core 拥有 BeanFactory,Spring Context 基于 Spring Core,它们是 IOC 容器。
AOP 切面编程
如果没有切面编程
public void embarkOnQuest() {
minstrel.singBeforeQuest();
quest.embark();
minstrel.singAfterQuest();
}
但骑士真的要管理吟游诗人吗?应交由 Spring 配置管理:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<!-- Spring AOP 依赖 AspectJ,不然会报 ReflectionWorldException-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.2</version>
<scope>runtime</scope>
</dependency>
<aop:config>
<aop:aspect ref="minstrel">
<!-- execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表)) -->
<aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/>
<aop:before pointcut-ref="embark" method="singBeforeQuest"/>
<aop:after pointcut-ref="embark" method="singAfterQuest"/>
</aop:aspect>
</aop:config>
模板
原始的 JDBC 连接
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- 在 Maven 仓库查询后,往下拉,可以看对应 mysql 的版本 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.22</version>
</dependency>
public Employee getEmployeeById(Long id) throws Exception {
/*
获取连接
方式一:注册驱动
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
方式二:连接池
//数据源配置
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1/db_student?serverTimezone=UTC");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //这个可以缺省的,会根据url自动识别
dataSource.setUsername("root");
dataSource.setPassword("abcd");
*/
Properties properties = new Properties();
InputStream is = EmployeeTest.class.getResourceAsStream("/druid.properties");
properties.load(is);
//返回的是DataSource,不是DruidDataSource
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = createPreparedStatement(conn, id);
ResultSet rs = stmt.executeQuery()) {
Employee employee = null;
if (rs.next()) {
employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setName(rs.getString("name"));
}
return employee;
}
}
private PreparedStatement createPreparedStatement(Connection conn, Long id) throws SQLException {
String sql = "SELECT id, name FROM employee where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setLong(1, id);
return ps;
}
url=jdbc:mysql://localhost:3306/test?useSSL=false
# 这个可以缺省的,会根据url自动识别
driverClassName=com.mysql.jdbc.Driver
username=root
password=root
# 初始连接数,默认0
initialSize=10
# 最大连接数,默认8
maxActive=30
# 最小闲置数
minIdle=10
# 获取连接的最大等待时间,单位毫秒
maxWait=2000
# 缓存PreparedStatement,默认false
poolPreparedStatements=true
# 缓存PreparedStatement的最大数量,默认-1(不缓存)。大于0时会自动开启缓存PreparedStatement,所以可以省略上一句设置
maxOpenPreparedStatements=20
使用 JdbcTemplate 模板
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcDemo {
private JdbcTemplate jdbcTemplate;
public JdbcDemo(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Employee getEmployeeById(Long id) {
return jdbcTemplate.queryForObject("SELECT id, name FROM employee where id=?",
// 将结果匹配为对象
new RowMapper<Employee>() {
@Override
public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setName(rs.getString("name"));
return employee;
}
}, 1L
);
}
}
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 配置数据源 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默认必须使用数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="jdbcDemo" class="sia.knights.JdbcDemo">
<constructor-arg ref="jdbcTemplate"/>
</bean>