注解式开发
注解的存在是为了简化XML的配置,Spring 6倡导全注解式开发
注解细节回顾
//元注解,标注注解的注解,表示可以出现在类上和字段上
@Target(value = {ElementType.TYPE, ElementType.FIELD})
//@Target({ElementType.TYPE, ElementType.FIELD})
//注解名为value,注解可以省略
//@Target(ElementType.TYPE)
//当数组中只有一个元素,大括号可以省略
@Retention(RetentionPolicy.RUNTIME)
//保持型策略,RUNTIME使注解保存在class文件中,被反射读取
public @interface Component {
//定义注解属性
String value();
String name();
}用注解标注User类
@Component(value = "123", name = "jack")
public class User {
}通过反射读取注解元素
public class TestBean {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.thrinisty.bean.User");
if(clazz.isAnnotationPresent(Component.class)) {
//获取类上的注解
Component annotation = clazz.getAnnotation(Component.class);
System.out.println(annotation.value());
System.out.println(annotation.name());
}
}
}注解声明原理
现在有一个包名,扫描包下的所有类,当类上有@Component注解的时候,实例化对象,然后放入Map集合中
public class TestBean {
public static void main(String[] args) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
//包名
String packageName = "com.thrinisty.bean";
//获取路径
String packagePath = packageName.replaceAll("\\.", "/");
URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
String path = url.getPath();//获取绝对路径
File file = new File(path);
File[] files = file.listFiles();
for (File f : files) {
String[] split = f.getName().split("\\.");
String className = packageName + "." + split[0];
//获取到包下的每个类全路径
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Component.class)) {
//获取注解
Component annotation = clazz.getAnnotation(Component.class);
String id = annotation.value();
Constructor<?> constructor = clazz.getConstructor();
Object object = constructor.newInstance();
map.put(id, object);
}
}
System.out.println(map.toString());
}
}@Component("userBean")
public class User {
}@Component("orderBean")
public class Other {
}public class Vip {
}被注解Component标注的会被反射实例化,并且放入map集合
{
orderBean=com.thrinisty.bean.Other@2e0fa5d3, userBean=com.thrinisty.bean.User@5010be6
}声明Bean注解
TIP负责声明Bean的注解,常见的包括四个
- @Component:组件
- @Controller:控制器
- @Service:业务
- @Repository:仓库
package org.springframework.stereotype;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}后面三个通过@AliasFor起一个别名,其实本实质上是组件,用哪一个都可以
package org.springframework.stereotype;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}为了增强代码的可读性:表示层建议用Controller,业务层建议用Service,数组操作层建议用Repository
使用方式
1.加入aop依赖
2.再Spring配置中添加context命名空间,并指定扫描的包
3.在Bean类上使用注解
<?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">
<!-- 给Spring指定要扫描那些包-->
<context:component-scan base-package="com.bean"/>
</beans>注解修饰类
@Component("userBean")
public class User {
}如果不给value赋值,默认以类的首字母小写命名(vip)
@Controller
public class Vip {
}测试程序,和之前一样
public class TestBean {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Vip vip = context.getBean("vip", Vip.class);
User user = context.getBean("userBean", User.class);
System.out.println(user.toString() + " " + vip.toString());
}
}多包扫描
package com.dao;
import org.springframework.stereotype.Component;
@Component
public class Manager {
}第一种解决方式:用,隔开即可
<context:component-scan base-package="com.bean, com.dao"/>第二种解决方式:指定父包
<context:component-scan base-package="com"/>选择性实例化
假设现在又很多Bean,被四种注解标注,现在需要实例化其中一种注解的Bean,而其余的不参与实例化
第一种解法
利用 use-default-filters=“false 使所有Bean注解失效
<context:component-scan base-package="com.bean" use-default-filters="false"/>再使用<context
<context:component-scan base-package="com.bean" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>第二种解法
先全部生效,再关闭不需要的注解
<context:component-scan base-package="com.bean" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>Component Manager
Component User测试程序
public class TestBean {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
}
}Controller Servlet
Controller Vip注入Bean注解
TIP负责注入Bean的注解,常见的包括四个
- @Value:专门用于注入简单类型(可以使用在属性和方法上)
- @Autowired: 通过类型自动装配
- @Qualifier:配合Autowired使用,在接口有不同实现类时候指定名字
- @Resource: jdk拓展,也是最推荐使用的复杂类型注入的方式
Value注解
用于注入简单类型
@Component
public class MyDataSource implements DataSource {
@Value("com.mysql,cj.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring6")
private String url;
@Value("root")
private String username;
@Value("123456")
private String password;
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
...
}通过构造方法参数赋值完成注入
@Component
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
public MyDataSource(@Value("com.mysql,cj.jdbc.Driver") String driver,
@Value("jdbc:mysql://localhost:3306/spring6")String url,
@Value("root")String username,
@Value("123456")String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}测试程序
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
MyDataSource dataSource = context.getBean("myDataSource", MyDataSource.class);
System.out.println(dataSource);
}MyDataSource{driver='com.mysql,cj.jdbc.Driver', url='jdbc:mysql://localhost:3306/spring6', username='root', password='123456'}Autowired、Qualifier注解
Autowired注解可以用来注入非简单类型,翻译为自动装配
单独使用Autowired注解,默认根据类型装配
如果需要根据名字进行装配,需要联合Qualifier注解使用
OrderDao接口
public interface OrderDao {
void insert();
}OrderDaoImplForMySQL实现类
@Repository("orderDaoImplForMySQL")
public class OrderDaoImplForMySQL implements OrderDao {
@Override
public void insert() {
System.out.println("MySQL insert order");
}
}服务类,拥有实现类对象接口,使用@Autowired自动装配
@Service("orderService")
public class OrderService {
@Autowired
private OrderDao orderDao;
public void generate() {
orderDao.insert();
}
}测试程序
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
OrderService service = context.getBean("orderService", OrderService.class);
service.generate();
}MySQL insert orderTIP如果类只有一个构造方法,且属性和参数能对应上,可以省略@Autowired注解
这种方式适用于一个接口对应的一个实现类,如果有两个实现类就不能完成自动装配,需要用Qualifier注解,实现按照名字装配
现有另外的一个NoSQL实现类
@Repository("orderDaoImplForNoSQL")
public class OrderDaoImplForNoSQL implements OrderDao {
@Override
public void insert() {
System.out.println("NoSQL Insert Order");
}
}注入的时候利用@Qualifier(“orderDaoImplForNoSQL”)指定注入的Bean对象
@Service("orderService")
public class OrderService {
@Autowired
@Qualifier("orderDaoImplForNoSQL")
private OrderDao orderDao;
public void generate() {
orderDao.insert();
}
}Resource注解
Resource注解可以完成非简单类型的注入,是Java标准规范的一部分,是标准注解,而Autowired注解是Spring框架自己的
Resource注解默认根据名字进行装配,未指定名字时,使用属性名作为name,找不到会自动启动类型装配
使用:直接指定名字完成按照名字装配
@Service("orderService")
public class OrderService {
@Resource(name = "orderDaoImplForNoSQL")
private OrderDao orderDao;
public void generate() {
orderDao.insert();
}
}全注解开发
可以通过一个类代替spring配置文件,以后不需要使用Spring配置文件
package com.bean3;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com")
public class SpringConfig {
}测试程序
@Test
public void testNoXML() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
OrderService service = context.getBean("orderService", OrderService.class);
service.generate();
}JdbcTemplate
是Spring提供的一个JDBC模板类,是对JDBC的封装,简化JDBC代码
除此之外,你还可以不使用它,而是用例如MyBatis等ORM框架,我还没有学过MyBatis,先从JdbcTemplate入门使用一下
基本使用
jdbc依赖以及mysql驱动
<!-- Spring JDBC (包含 spring-jdbc 和 spring-tx) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.9</version> <!-- 使用最新稳定版 -->
</dependency>
<!-- MySQL Connector/J -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version> <!-- 使用最新稳定版 -->
</dependency>实现一个DataSource的实现类,重写getConnection方法,并纳入Spring管理
public class MyDateSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
@Override
public Connection getConnection() throws SQLException {
try {
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
} catch (Exception e) {
throw new RuntimeException(e);
}
}Spring配置:配置了一个ds数据源Bean对象,注入各个参数,用内置的jdbcTemplate对象传入ds Bean对象到 dataSource参数
<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="ds" class="bean.MyDateSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="654321"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>
</beans>测试程序:增加数据
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into user (id, name, password) values (?, ?, ?)";
int update = jdbcTemplate.update(sql, 3, "Lory", "135788");
System.out.println(update);
}1测试程序:修改数据
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "update user set name=? where id=?";
int update = jdbcTemplate.update(sql, "Joker", 1);
System.out.println(update);
}1测试程序:删除数据
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "delete from user where id=?";
int update = jdbcTemplate.update(sql, 3);
System.out.println(update);
}1测试程序:查询单个数据
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select * from user where id = ? ";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);
System.out.println(user);
}User{id=1, name='Joker', password='123456'}测试程序:多个查询结果,返回List集合
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select * from user";
List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
for(User user : list){
System.out.println(user);
}
}User{id=1, name='Joker', password='123456'}
User{id=2, name='李四', password='654321'}测试程序:查询单个值
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select count(1) from user";
Integer integer = jdbcTemplate.queryForObject(sql, int.class);
System.out.println(integer);
}整合Druid连接池
引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.18</version> <!-- 请使用最新稳定版 -->
</dependency>Spring配置
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="654321"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>使用结果:看到输出了一些日志信息
21:28:46.235 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1} inited
2