2、spring框架-依赖注入
spring框架-依赖注入
注解方式实现IOC和DI
@Component
控制反转的实现,加上这个注解后,spring根据包扫描会自动的扫描到这个注解将这个类创建对象交给spring容器。
xml方式:需要引入aop的架包
//包扫描 <context:component-scan base-package="cn.zj.spring"/>
@Component
注解会在spring容器创建的时候扫描到这个注解,spring会为它创建对象
默认情况下名字是类名首字母小写 比如:userService
有一个value属性可以配置对当前的对象取name,相当于
@Component(value = "s1,s2,s3")
简写:
@Component
("s1,s2,s3")
还有其他的跟@Component功能相同的注解,用语义来区分。
@Controller
:用于web层
@Service
:用于服务层
@Repository
:用于持久层
一般来说都是可以在其他层的,只是从语义上更好的理解该类的作用。
@Controller
在springMcv
的时候只能用这个
@Autowired
+@Qualifier
/* * DI依赖注入的方式: * @Autowired+@Qualifier * @Resource * * @Autowired:类型注入,类型当前类的类型自动去spring容器中找对象 * 如果接口有多个实现类,就会抛:No qualifying bean of type 异常 * 解决方案: * 加上@Qualifier注解,指定注入的是哪个对象。 * @Qualifier(value = "service1"): * value可省略,用来指定类型注入的是哪一个对象 * 如果没有找到所要注入的对象会报:NoSuchBeanDefinitionException: No qualifying bean of type * * 解决方式: * @Autowired(required = false),虽然spring的问题解决了,但是 * 你使用了对象,就会报:java.lang.NullPointerException * * * 类型注入的几种方式: * 1.字段的注入,注解直接贴到字段上或叫成员变量 * 2.方法的注入:注解直接贴到方法上面 * 3.构造函数注入:直接写一个有参数的构造函数 * 注意:构造函数注入不用写@Autowired,默认就是使用这个,就算写了也不起作用 */ //1.字段的注入 //@Autowired //@Qualifier(value = "service1") private UserService userService; //2.方法的注入 //@Autowired //@Qualifier("service2")//有多个实现类 public void setUserService(UserService userService) { this.userService = userService; } //3.构造函数注入,多个实现类需要放在参数上@Qualifier("service2") public UserController(@Qualifier("service2") UserService userService) { this.userService = userService; }
@Resource
/* * @Resource注解也可以注入对象,是java官方提供的,spring只是实现了其功能 * 如果有多个实现类,需要用到name属性:@Resource(name = "service2") * * 注入的方式: * 1.字段注入 * 2.方法注入 * @Resource没有构造器注入,跟@Autowired不同 */ //1.字段注入 //@Resource(name = "service2") private UserService userService; //2.方法注入 @Resource(name = "service1") public void setUserService(UserService userService) { this.userService = userService; }
全注解的方式
我们发现,之所以我们现在离不开xml配置文件,是因为我们有一句很关键的配置:
<context:component-scan base-package="cn.zj.spring"> </context:component-scan>
如果他要也能用注解配置,那么我们就可以脱离xml文件了。
通过@Configuration注解和@ComponentScan注解
替换XML配置文件的@Configuration注解 | @Configuration配置类注解,在纯注解配置中,类加了该注解,就意味着该类是Spring的配置类。该类的功能就是用于替代原来的XML配置文件。 作用: 用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationConfigApplicationContext(有@Configuration注解的类.class)。 |
---|---|
@ComponentScan注解 | @ComponentScan注解扫描类,作用就是配置扫描Spring组件类的路径。功能等同原来配置文件的--作用: 用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的:<context:component-scan base-package="cn.zj.spring"/>是一样的。--属性: basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。 |
@PropertySource注解 | 作用: 用于加载.properties文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties配置文件中,就可以使用此注解指定properties配置文件的位置。属性: value[]:用于指定properties文件位置。如果是在类路径下,需要写上classpath: |
@Bean注解 | 作用: 该注解只能写在方法上,使用此方法创建一个对象,并且放入spring容器。它就相当于我们之前在xml配置中介绍的<bean标签> 属性: name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。 |
@Import注解 | 作用: 用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解。当然,写上也没问题。 |
@Value注解 | 读取properties配置文件以后, 使用 ${key}获取配置文件中对应的值 @Vlaue(“${jdbc.dirverClassName}”)private String driverClassName; |
package cn.zj.spring.config; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import com.alibaba.druid.pool.DruidDataSource; import com.spring.Config.SpringConfig2; /* * @Configuration 告诉程序我是Spring容器 * ------------------------------------------------------------ *@ComponentScan(basePackages = {"cn.zj.spring"}) * @ComponentScan:是用来配置需要扫描包,然后扫描类中的注解, * 与<context:component-scan base-package="cn.zj.spring"/>功能相同。 *可以配置扫描多个包。 * 简写: * @ComponentScan("cn.zj.spring") *------------------------------------------------------------ * @PropertySource:读出xxx.properties的配置文件的注解 * classpath:固定写法,是用来读取配置文件的 * 相当于:<context:property-placeholder location="classpath:db.properties" /> *----------------------------------------------------------- * @Value : 根据key获取配置文件中的value。有一个value的属性可省略 * 还可以写字符串或基本数据类 * 读value使用spring的EL表达式 ${key} *----------------------------------------------------------- *在new AnnotationConfigApplicationContext(class<T>...),可以传入多个配置类 *@import:导入其他配置类的类 *@Import(SpringConfig2.class),传入一个字节码文件,.class * 相当于:<import resource=""/> */ @Configuration //@ComponentScan(basePackages = {"cn.zj.spring"}) @ComponentScan("cn.zj.spring") @PropertySource("classpath:db.properties") @Import(SpringConfig2.class) public class SpringConfig { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Value("10") private int maxActive; /* * 数据库连接池的创建 * * @Bean:告诉spring容器我是一个需要创建的对象,也叫方法注入 * 告诉spring容器我需要创建对象,相当于 <bean/> * @Scope:还可以在下面设置对象是单例还是多例 * */ @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close") @Scope public DataSource getDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(this.driverClassName); dataSource.setUrl(this.url); dataSource.setUsername(this.username); dataSource.setPassword(this.password); dataSource.setMaxActive(this.maxActive); return dataSource; } }
spring测试
在spring中集成了单元测试的包,我们在junit测试的时候总是要写很多次配置文件,还有写从spring容器中获取对象。图解如下:
传统的测试:
存在问题:
1,每个测试都要重新启动Spring容器,启动容器的开销大,测试效率低下。
2,不应该是测试代码管理Spring容器,应该是Spring容器在管理测试代码。
正确的使用方式:
将junit交给spring容器管理,而不是spring容器在junit的一个方法中,交给spring管理后,junit与bean之间就可以相互注入了。
执行步骤:
- 导入test依赖包
-
使用
@RunWith
注解//将Junit交给spring容器 @RunWith(SpringJUnit4ClassRunner.class) -
使用
@ContextConfiguration
注解//加载配置文件 @ContextConfiguration(classes = SpringConfig.class)
注意:
Spring测试必须保证Eclipse的单元测试的最低版本是 4.12版本,如果使用的Eclipse版本很低,那么单元测试版本可能低于4.12,那么需要开发者手动导入单元测试的jar包
案例:
//先让junit在spring容器中运行 @RunWith(SpringJUnit4ClassRunner.class) //加载配置文件 @ContextConfiguration(classes = SpringConfig.class) public class UserTest { @Autowired private UserController userController; @Test public void testBean() { userController.say(); } @Test public void testInsert() { userController.insert(); } }
spring-jdbc
Spring对象也支持JDBC,对JDBC只进行了薄薄的一层封装
问题: Java开发已经有JDBC,为什么Spring还要支持JDBC操作呢?
最重要的原因: Spring操作JDBC能自动管理事务
步骤:
- 导入依赖包spring-jdbc,spring-tx
-
创建JdbcTempalte对象在spring容器中
//获取springJdbcTempalte @Bean @Scope("prototype") public JdbcTemplate getJdbcTemplate() { JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSource()); return jdbcTemplate; } -
操作数据库的增删改查
package cn.zj.spring.dao.impl; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import cn.zj.spring.dao.UserDao; import cn.zj.spring.pojo.User; @Repository public class UserDaoImpl implements UserDao { // 获取JdbcTempalte @Autowired private JdbcTemplate jdbcTemplate; @Override public void insert(User user) { String sql = "insert into t_user(name,email) values(?,?)"; this.jdbcTemplate.update(sql, user.getName(), user.getEmail()); } @Override public void deleteByPrimaryKey(Integer id) { String sql = "delete from t_user where id = ?"; this.jdbcTemplate.update(sql, id); } @Override public void updateByPrimaryKey(User user) { String sql = "update t_user set name = ?, email = ? where id = ? "; this.jdbcTemplate.update(sql, user.getName(), user.getEmail(), user.getId()); } @Override public User selectByPrimaryKey(Integer id) { String sql = "select * from t_user where id = ?"; Object[] objs = { id }; User user = this.jdbcTemplate.queryForObject(sql, objs, new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int idx) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setEmail(rs.getString("email")); return user; } }); return user; } @Override public List<User> selectList() { String sql = "select * from t_user"; List<User> users = this.jdbcTemplate.query(sql, new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int arg1) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setEmail(rs.getString("email")); return user; } }); return users; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!