ioc 控制反转
一个dao接口多个实现类,service层的实现类中创建了接口对象调用接口的方法,配置文件来决定该接口对象的实现类,通过实现类去调用实际的方法。
IOC DI注入
beans.xml
<?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">
<!--
创建对象的第一种方式:利用无参构造器
id:唯一标识符
class:类的全类名
name:起别名 可以支持起多个别名 用逗号 空格 分号隔开 可混合使用
import:将多个配置文件合并
-->
<bean id="baseDaoImpl" class="com.lee.mapper.impl.BaseDaoImpl" name="baseDao"></bean>
<bean id="userDaoImpl" class="com.lee.mapper.impl.UserDaoImpl" name="userDao"></bean>
<!--BaseServiceImpl中有一个BaseDao 接口对象,但是没有还未赋值-->
<bean id="baseServiceImpl" class="com.lee.service.impl.BaseServiceImpl">
<!--给属性赋值 创建baseDaoImpl对象或者userDaoImpl,默认无参构造-->
<property name="iBaseDao" ref="userDaoImpl"></property>
<!--通过下标给有参构造赋值 前提是显示写出了有参构造-->
<constructor-arg index="0" ref="userDaoImpl"></constructor-arg>
<!--通过参数名给有参构造赋值 前提是显示写出了有参构造-->
<constructor-arg name="iBaseDao" ref="userDaoImpl"></constructor-arg>
<!--通过有参构造器参数个数注入 得显示写出来-->
<constructor-arg value="sss"/>
<constructor-arg value="sz"/>
<!-- 不推荐使用 通过类型给有参构造赋值 前提是显示写出了有参构造-->
<constructor-arg type="com.lee.mapper.IBaseDao" ref="baseDaoImpl"></constructor-arg>
</bean>
<!--给 id为userDaoImpl起别名为userDao-->
<alias name="userDaoImpl" alias="userDao"></alias>
<bean id="address" class="com.lee.entity.Address">
<property name="address" value="西安"></property>
</bean>
//Student中有一个类属性 Address address;
<bean id="student" class="com.lee.entity.Student" >
<!--给student属性赋值-->
<property name="name" value="zs"></property>
<!--对象-->
<property name="address" ref="address"></property>
<!--数组-->
<property name="books">
<array>
<value>红楼梦</value>
<value>红楼梦</value>
</array>
</property>
<!--list-->
<property name="hobbys">
<list>
<value>游泳</value>
<value>篮球</value>
</list>
</property>
<!--set-->
<property name="games">
<set>
<value>lol</value>
<value>dota</value>
</set>
</property>
<!--map-->
<property name="card">
<map>
<entry key="身份证" value="111111"></entry>
<entry key="银行卡" value="22222"></entry>
</map>
</property>
<!--properties-->
<property name="info">
<props>
<prop key="id">1190</prop>
<prop key="school">xxxx</prop>
</props>
</property>
<!--给空或空字符串-->
<!--<property name="wife" value=""></property>-->
<property name="wife">
<null></null>
</property>
<!--c和p命名空间,c构造器注入,p属性注入 需要导入约束 了解即可-->
</bean>
<!--自动注入通过autowire 设置byName,通过名字自动注入,需要保证id和对象set方法的参数名一致
byType 通过类型自动注入,可以没有id,但是类型重复就无法自动注入
-->
<bean id="dog" class="com.lee.entity.Dog"></bean>
<bean id="people" class="com.lee.entity.People" autowire="byName">
<property name="name" value="zs"></property>
<!--通过属性注入-->
<!--<property name="dog" ref="dog"></property>-->
</bean>
</beans>
通过注解的方式注入
beans.xml
<?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.lee"></context:component-scan>
<context:annotation-config/>
<!--导入注解的约束和 <context:annotation-config/> -->
<!--people类的dog属性加入@Autowired注解 通过byType方式注入-->
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class People {
@Value("zs")
private String name;
private Integer age;
//@Resource
@Autowired
@Qualifier(value = "dog22")
//加入Qualifier相当于给dog取别名dog22,通过dog22找到id为dog22的bean
private Dog dog;
}
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
<!--
@Qualifier(value = "dog22")和Autowired注解一起使用 注入到下面的dog22,当名字不一致的时候使用
此时Dog 属性名为 dog
bean id="dog22" class="com.lee.entity.Dog"/>
@Autowired里面的required设置为false代表可以为null
@Nullable注解代表可以为null
@Resource结合了spring的注解,先通过属性名字dog ,ref到id为dog的Dog类 没找到再通过类型注入
等价于<property name="dog" ref="dog"/>
@Scope("singleton")设置作用域为单例,默认单例 prototype为原型
id作用用于getBean时的标识和ref寻找
-->
<bean id="dog" class="com.lee.entity.Dog" scope="singleton"></bean>
<!--<bean id="dog22" class="com.lee.entity.Dog"></bean>-->
<bean id="people" class="com.lee.entity.People"></bean>
<!--使用spring注解开发代替bean 还需要导入扫描器
<context:component-scan base-package="com.lee"></context:component-scan>
@Component说明这个类被spring管理了
@Value赋值
dao层用@Repository
service层用@Service
Servlet层用@Controller-->
</beans>
@Test
public void test2(){
//通过new ClassPathXmlApplicationContext("beans.xml");实现配置文件
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
//getBean(id唯一标识)
People people = context.getBean("people", People.class);
people.getDog().shout();
System.out.println(people.toString());
}
注解完全代替xml
需要定义一个JavaConfig类代替xml
//@Configuration注解定义一个配置类完全代替beans.xml,是一个JavaConfig
@Configuration
public class LeeConfig {
/**
*
* @Bean等价于bean
* 返回值类型 等价于class=com.lee.entity.Cat
* 方法名 等价于id =getCat
* 即 <bean id="getCat" class="com.lee.entity.Cat"></bean>
*/
@Bean
public Cat getCat(){
return new Cat();
}
}
@Component//定义这个类被spring管理
@ComponentScan("com.lee")//扫描器
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Cat {
@Value("miao")
private String name;
@Value("18")
private Integer age;
}
@Test
public void testJavaConfig(){
//通过new AnnotationConfigApplicationContext(LeeConfig.class); 实现JavaConfig
ApplicationContext context = new AnnotationConfigApplicationContext(LeeConfig.class);
//getBean(id唯一标识),在LeeConfig这个配置类中定义了
Cat getCat = (Cat) context.getBean("getCat");
System.out.println(getCat);
}
AOP
代理实现:难点
public interface IBaseService {
public void show();
}
@Data
public class BaseServiceImpl implements IBaseService {
private IBaseDao iBaseDao=new BaseDaoImpl();
public BaseServiceImpl() {
}
public BaseServiceImpl(IBaseDao iBaseDao) {
this.iBaseDao = iBaseDao;
}
@Override
public void show() {
iBaseDao.show();
}
}
IBaseService接口需要代理,BaseServiceImpl 是个真实对象,现在需要一个代理类去代理BaseServiceImpl
引入了一个InvocationHandler,去实现代理
public class ProxyInvocation implements InvocationHandler {
//被代理的接口
private IBaseService baseService;
//赋值
public void setBaseService(IBaseService baseService) {
this.baseService= baseService;
}
//通过接口得到动态代理类的对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),baseService.getClass().getInterfaces(),this);
}
//自动调用这个invoke方法 返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(baseService,args);
}
}
测试:
@Test
public void test1(){
//得到真实的对象
BaseServiceImpl baseServiceImpl=new BaseServiceImpl();
//得到类的对象
ProxyInvocation pih = new ProxyInvocation();
//给对象的代理接口赋值
pih.setTarget(baseServiceImpl);
//通过接口得到一个代理对象
IBaseService proxy = (IBaseService) pih.getProxy();
//代理对象调用方法
proxy.show();
}
匿名内部类
@Test
public void test(){
//jdk的代理
Host host=new HostImpl();
InvocationHandler in=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("发布信息");
System.out.println("带人看房");
host.hire();
return null;
}
} ;
Host o = (Host) Proxy.newProxyInstance(this.getClass().getClassLoader(), host.getClass().getInterfaces(),in);
o.hire();
}
@Test
public void test2(){
Host host=new HostImpl();
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(HostImpl.class);
enhancer.setCallback(new org.springframework.cglib.proxy.InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("发布信息");
System.out.println("带人看房");
host.hire();
return null;
}
});
Host o = (Host) enhancer.create();
o.hire();
}
通用方法
public class ProxyInvocation implements InvocationHandler {
//被代理的接口
private Object target;
//赋值
public void setTarget(Object target) {
this.target = target;
}
//通过接口得到动态代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//自动调用这个invoke方法 返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target,args);
}
}
AOP增强即面向切面
加入日志 pom里面需要配置
方式一
实现MethodBeforeAdvice
public class Log implements MethodBeforeAdvice {
//AfterReturningAdvice之后
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("执行了"+method.getName());
method.invoke(o,objects);
}
}
xml需要配置
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描器 扫描哪些包下的注解-->
<context:component-scan base-package="com.lee"></context:component-scan>
<context:annotation-config/>
<bean id="div" class="com.lee.entity.DivLog"></bean>
<bean id="log" class="com.lee.entity.Log"></bean>
<bean id="baseService" class="com.lee.service.impl.BaseServiceImpl"></bean>
<!--log类继承 切入点 环绕-->
<aop:config>
//expression决定切入的哪个类 哪个方法 参数
<aop:pointcut id="pointcut" expression="execution(* com.lee.service.impl.BaseServiceImpl.*(..))"></aop:pointcut>
//advice-ref 引用的类 切入到哪里
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
自定义类日志
public class DivLog {
public void before(){
System.out.println("方法执行之前");
}
public void after(){
System.out.println("方法执行之后");
}
}
<bean id="div" class="com.lee.entity.DivLog"></bean>
<bean id="log" class="com.lee.entity.Log"></bean>
<bean id="baseService" class="com.lee.service.impl.BaseServiceImpl"></bean>
<!--自定义log类 切面编程 切入点 切面-->
<aop:config>
//自定义的需要ref到类中
<aop:aspect ref="div">
//id为pointcut expression决定切入的类 方法 参数
<aop:pointcut id="pointcut" expression="execution(* com.lee.service.*.*(..))"></aop:pointcut>
//方法执行之前 pointcut-ref决定切入的地方id
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
<!--注解方式实现切面 加入注解即可@Aspect @Before @After 和@Before("execution(* *.*(..))")-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
注解的方式实现日志,需要在配置文件中把这个类配置好然后使用<aop:aspectj-autoproxy></aop:aspectj-autoproxy>即可
@Aspect
@Component
public class Log {
//定义切入点
@Pointcut("execution(* service.impl.UserServiceImpl.*(..))")
public void a(){}
@Before("a()")
public void before(JoinPoint joinPoint){
System.out.println("注解前置通知");
}
@AfterReturning(value = "a()",returning = "obj")
public void after(JoinPoint joinpoint,Object obj){
System.out.println("注解后置通知");
}
//异常通知
@AfterThrowing(value = "a()",throwing = "ex")
public void thr(JoinPoint joinPoint,Exception ex){
System.out.println("异常通知");
}
//环绕通知
@Around(value = "a()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前置");
//放行
Object proceed = joinPoint.proceed();
System.out.println("环绕后置");
return proceed;
}
}
@Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//因为加入了aop面向切面编程,使用了动态代理,所以要转成接口IBaseService而不是BaseServiceImpl,BaseServiceImpl会报错
// BaseServiceImpl baseService = context.getBean("baseService", BaseServiceImpl.class);
IBaseService baseService = context.getBean("baseService", IBaseService.class);
baseService.show();
}