Spring基础知识
1. IoC 与依赖注入(DI)的关系
IoC思想是把实例化对象的权力交给spring框架去做,而依赖注入则是IoC思想的具体实现
例:以前创建对象:
public static void main(String[] args) {
User user=new User();
user.setName("FreeMan");
user.setSex("man");
}
现在由Spring在applicationContext.xml创建对象:
<bean id="user" class="com.pojo.User">
<property name="name" value="FreeMan"/>
<property name="sex" value="Man"/>
</bean>
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
User user =(User) app.getBean("user");
System.out.println(user);
结果对比:
以前调用dao层方法:
public class UserServiceImp implements UserService {
/**
* 显示
*/
@Override
public void show() {
UserDaoImp userDaoImp= new UserDaoImp();
userDaoImp.show();
}
}
现在通过spring 调用dao层方法
<!-- 通过配置bean 把 UserDaoImp对象放入Spring 容器里面-->
<bean id="daoImp" class="com.dao.impl.UserDaoImp"></bean>
<!-- 通过配置bean 把 UserServiceImp对象放入Spring 容器里面-->
<bean id="serviceImp" class="com.service.impl.UserServiceImp">
<!-- 通过属性注入 把UserDaoImp对象注入到 service 层中 -->
<property name="userDao" ref="daoImp"/>
</bean>
public class UserServiceImp implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
/**
* 显示
*/
@Override
public void show() {
userDao.show();
}
}
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserServiceImp bean = app.getBean(UserServiceImp.class);
bean.show();
}
}
这样对比下来感觉上是 Spring 的要稍微复杂一点,但是原始的方法中 UserDaoImp 与service层耦合死了,不利于代码的修改,但spring的对象注入则是能够解决这种耦合,使之能够松耦合
得到Spring容器里面的对象并运用;
注意的是利用以上方法调用对象方法时 必须要有set方法才行
应为xml的property标签是根据属性set方法注入的
以上只是简单的了解一下spring的作用
2. applicationContext.xml基本知识
对于 Spring的配置文件里面有很深的门道,就说一下我对它的理解
首先是spring 配置的头文件,有很多命名空间 例如 context,p,mvc,aop命名空间等
如何快速配置命名空间只需要两步:
第一步
第二步
需要加什么命名空间则直接在把刚刚复制的内容里含有beans的单词替换成自己想要的就行
例如context:
bean标签基本属性详解:bean标签把 对象放入到spring容器中
id 属性则是在Spring容器中唯一标识符,可随意写,但建议命名为类相关的名字。
class 属性:确定该类的位置。意思是告诉Spring容器,帮我实例对象的类所在位置。
property标签:设置该类的属性的是
name 则是告诉spring容器 你要设置的该类里面的那一个属性, 后面一般 value,或者ref
value属性 是给属性一般赋值
ref则是给属性 赋引用型的值一般是另一个类的实例化对象
使用构造方法进行注入
constructor-arg 标签:
name 构造方法形参名
value:赋值
ref:赋引用对象名
init-method:指定类中的初始化方法名称(前提是类方法里有方法)
destroy-method:指定类中销毁方法名称)(前提是类方法里有方法)
scope:singleton(只创建一个实例对象)无论调用多少次该对象就只有一个(在你配置好后,类加载完成就创建对象)
scope:prototype(创建多个实例对象),你调用一次该对象,他会在你调用时创建对象
如以下代码示例 ref 属性的用法:
<!-- 通过配置bean 把 UserDaoImp对象放入Spring 容器里面-->
<bean id="daoImp" class="com.dao.impl.UserDaoImp"></bean>
<!-- 通过配置bean 把 UserServiceImp对象放入Spring 容器里面-->
<bean id="serviceImp" class="com.service.impl.UserServiceImp">
<!-- 通过属性注入 把UserDaoImp对象注入到 service 层中 -->
<property name="userDao" ref="daoImp"/>
</bean>
此ref=id是daoImp的实例化对象
如果属性里面有list属性则可使用
<bean id="user" class="com.pojo.User">
<property name="name" value="FreeMan"/>
<property name="sex" value="Man"/>
<property name="list">
<!-- value-type属性是填写list属性存储的值的路径-->
<list value-type="">
<!-- 可以直接 填写已经注入的id-->
<ref bean="daoImp"/>
<!-- 也可以填写需要存放对象的路径地址-->
<bean class="com.pojo.User" id="user2">
<!-- 在里面也可以继续赋值得 引用类型的对象-->
<property name="sex" value="Man"/>
</bean>
<!-- 也可以填写自己想要的值-->
<value>1</value>
</list>
</property>
</bean>
属性里有map 的配置如下
<property name="map" >
<!-- map可以设置键值对的类型 第一个value-type则是设置 值 第二行是key-type 键的类型-->
<map value-type="" key-type="" >
<!--设置普通的键值对象
Map map=new Map()
map.put("a","97")
-->
<entry key="" value=""/>
<!-- 设置引用类型的简直对象
Map map=new Map()
map.put(Object,Object)
-->
<entry key-ref="" value-ref=""/>
</map>
3. IoC的新旧注解基础知识
使用注解需要在applicationContext.xml配置注解扫描:
context命名空间下的标签: <context:component-scan>(注解扫描),
<context:property-placeholder>(加载配置文件标签)
<!-- base-package="" 需要扫描的那些包,多个包 ,可以用逗号隔开 进行多包扫面-->
<context:component-scan base-package="com"/>
<!-- 一定要加上classpath: 否则可能会报错-->
<context:property-placeholder location="classpath:jdbc.properties"/>
Spring的原始注解理解:
原始注解有:
@Component //使用在类上用于实例化Bean(可以在该注解写上该bean id的值)@Component("")
@Controller //使用在web层类上用于实例化Bean
@Service //使用在service层类上用于实例化Bean
@Repository //使用在dao层类上用于实例化Bean
//上面四个都是直接把类实例化到Spring容器中四个都一样的有用只是分管的区域不一样,
//为了好区分该实例化层是具体的那一次就造就了这三个注解@Controller ,@Service,@Repository。
@Autowired //使用在字段上用于根据类型依赖注入(前提是该字段在spring容器中存在实例化的bean)
//如果Spring容器中有多个的同类型的实例化bean 例如 有俩个同类型的bean. id=user1, id=user2
//就必须在该字段上+@Autowired @Qualifier("user1")指定id
@Qualifier //结合@Autowired一起使用用于根据名称(bean的id)进行依赖注入
@Resource //相当于@Autowired+@Qualifier,按照名称(bean的id)进行注入
//可在字段上注入值 也可以注入spring 加载后端properties文件的值
//例如@value("${properties文件里的键名称}")
@Value //注入普通属性
@Scope //标注Bean的作用范围(之前的 singleton,prototype属性)
@PostConstruct //使用在方法上标注该方法是Bean的初始化方法
@PreDestroy //使用在方法上标注该方法是Bean的销毁化方法
新注解:
@Configuration //用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan
//用于指定 Spring 在初始化容器时要扫描的包。
//作用和在 Spring 的 xml 配置文件中的context:component-scan base-package="com"/>一样
@Bean //使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource //用于加载.properties 文件中的配置
@Import //用于导入其他配置类
properties文件内容
jdbc.user=root
主配置类
@Configuration//声明配置类
@ComponentScan("com")//告诉Spring 需要扫那些包,多个包可用逗号隔开
@Import(SprringConfig_1.class)//导入其它配置类
public class SpringConfig {
@Bean//该bean的默认id是 方法名;
public User getUser(){
User user=new User();
user.setName("FreeMan");
return user;
}
}
另一个配置类
@Configuration//配置类
@PropertySource("classpath:jdbc.properties")//加载properties文件
public class SprringConfig_1 {
@Value("${jdbc.user}")//把properties文件赋值给该字段
private String name;
@Bean("user")//在添加一个bean为user的对象到容器中
public User userTest(){
User user=new User();
user.setName(name);
return user;
}
}
测试name属性为FreeMan,以及name属性为root
的User对象是否注入到Spring框架
public class SpringUserTest {
public static void main(String[] args) {
// 注解加载配置类
AnnotationConfigApplicationContext app =
new AnnotationConfigApplicationContext(SpringConfig.class);
//测试两个配置文件里的user对象是否注入并能否取出
User bean = (User) app.getBean("user");
User bean1 = (User) app.getBean("getUser");
System.out.println("我是注解Bean为默认值方式注入的 "+bean.getName());
System.out.println("我是注解Bean为(user)方式注入的 "+bean1.getName());
}
}
结果如下:
上面内容就是SpringIoC 需要记住的基本知识
4.Spring测试
需要导入Spring与junit集合的测试依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
我直接在我的测试类上修成Spring -test集成的代码
//或者@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
//加载是Spring配置类
@ContextConfiguration(classes = SpringConfig.class)
/*加载Spring配置文件
注意加载文件 与加载配置类的区别
配置类不是字符串型,直接括号里面写配置类的字节码文件但要加前缀 classes=配置了类名.class
文件是字符串型 字符串前面还需要带上classpath:
@ContextConfiguration("classpath:applicationContext.xml")
*/
public class SpringUserTest {
// 因为这里有两个同类型的bean 我就使用Resource进行注入
//也可以是使用
// @Autowired
// @Qualifier("user")
@Resource(name = "user")
private User user;
@Autowired
@Qualifier("getUser")
private User getUSer;//注入另外一个bean
@Test//在方法上直接定义一个Test注解可直接运行方法
public void testBean(){
System.out.println("注解@Bean('user')的bean的值====>"+user.getName());
System.out.println("注解@Bean()的bean的值====>"+getUSer.getName());
}
}
结果如下
5. AOP切面
首先需要导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
①什么是aop
面向切面编程,不修改源代码对方法增强,减少代码重复,两种动态代理技术
jdk动态代理基于接口代理
cglib动态代理基于父类代理
需要掌握的术语点
连接点:方法(可理解为所有的方法都是连接点)
切入点:需要增强的方法(可理解为:即将要将该方法增强)
通知/增强:检查到目标方法将要执行时,执行的方法(可理解为,我在切入点周围加入的另一个方法,这个方法作用是切入点方法的增强)
切面:通知/增强+切入点
②aop 的xml配置
<!-- 把目标bean注入到Spring容器中-->
<bean id="targetImp" class="com.aop.TargetImp"/>
<!-- 把通知类/增强类 注入spring容器-->
<bean id="aspectConfig" class="com.aop.AspectConfig"/>
<!-- 开始把通知类增强类进行配置-->
<aop:config>
<!-- 告诉spring 我的通知类是什么-->
<aop:aspect ref="aspectConfig">
<!--告诉spring我的要增强的方法的具体位置
expression="execution()"切面表达式-->
<aop:pointcut id="point" expression="execution(* com.aop.Target.*(..))"/>
<!--aspectConfig 类里面的方法 adviceMethod()监听到切点表达式里的方法执行时,
告诉spring容器 我的 adviceMethod()方法在哪里执行-->
<aop:after method="adviceMethod" pointcut-ref="point"/>
</aop:aspect
<!--以a方法作为(通知)方法
aop:after method="a" 监测的方法之后(无论是否异常)都执行方法a -->
<!-- <aop:around method="a"监测的方法之前以及之后执行a方法-->
<!-- <aop:after-throwing method="a"监测的方法异常后执行该方法-->
<!-- <aop:before method="a"监测的方法之前执行a方法-->
<!-- <aop:after-returning method="a"监测的方法之后执行a方法-->
</aop:config>
我xml的配置是把切点表达式给抽取出来了,也可以在 配置增强方法 加上单独的切点表达式
如下:
<aop:after-returning method="adviceMethod" pointcut="execution(* com.aop.*.*(..))"/>
aop的xml配置需要注意事项:
切点表达式的使用
切点表达式尤其重要:
*
号代表的是任意 包名前的*
代表的是任意返回值 (注意返回值的这个*
号与包名要有空格) ..
代表的是当前包下及其子包下的所有 包名后的.*
代表当前包下的所有类 ,类名后的 .*
代表当前类下的所有方法 , 方法后的(..)
代表是任意参数
举例:
*
com.dao.imp.*
UserDaoImp.*
(..
):表示 任意返回值 com包下的dao包下的imp包下的UserDaoImp类下的任意方法的任意参数*
com.dao.imp.*
.*
(..
) 任意返回值 com包下的dao包下的imp包下的任意类下的任意方法的任意参数*
com.dao..
*
.*
(..
) 任意返回值 com包下的dao当前包及其子包的任意类下的任意方法的任意参数- 切点表达试还有很多。就不列举了
aop的配置结构
<aop:config
<aop:pointcut id=“” expression=“execution()”
<aop:aspect
<aop:before //<aop :after(等增强方法的具体执行位置)
</aop:config
6. SpringAOP注解
aop需要使用到的注解
通知类
@Aspect//告诉Spring这是aop通知类
@Component//注入bean
public class AspectConfigs {
//抽取切点表达式
// 在一个空方法上加上@Pointcut注解写上表达式
@Pointcut("execution(* com.aop.anno.*.*(..))")
public void pointExpression() {
}
//前置增强方法
@Before("pointExpression()")
public void before() {
System.out.println("前置的增强");
}
// 环绕增强方法
@Around("pointExpression()")
// 环绕增强需要使用ProceedingJoinPoint接口定义环绕前与环绕后的逻辑
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强");
Object proceed = joinPoint.proceed();
System.out.println("环绕后增强");
return proceed;
}
}
需要增强的方法类(目标对象类)
@Component
public class TargetAnno {
public void show() {
System.out.println("该方法要增强......");
}
}
Spring配置类
@Configuration//配置类
@ComponentScan("com.aop.anno")//扫包
@EnableAspectJAutoProxy//开启自动代理,spring才会进行方法的增强
public class SpringAopAnnoConfig {
}
@Component(在类上写)前面提到就不说了
@Acpset:(在通知类名上写)告诉spring这是我的增强类
@Pointcut:(在空方法上写,应为注解必须要有依赖,不能单独存在),抽取切点表达式
@Before,@Round,@After(在通知类里的方法上写)告诉Spring增强方法的类型
@EnableAspectJAutoProxy(在类上写),告诉Spring 开启自动代理
最后总结一看所有用到的注解
希望看到这些注解都知道是什么意思
Ioc常用注解
@Component
@Controller
@Service
@Repository
@Autowired
@Qualifier
@Resources
@Value
@Scope
@PostConstruct
@PreDestory
@Configuration
@Bean
@ComponentScan
@PropertySource
@Import
测试常用注解
@Runwith
@ContextConfiguration
Aop常用注解
@Aspect
@EnableAspectjAutoProxy
@Ponitcut
@Before
@After
@AfterThrowing
@AfterReturning
@Around