Spring学习完个人总结

Spring框架学习心得(1) Spring <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> 阅读详情

Spring个人总结

通过观看尚硅谷的视频学习完,自己在学习中归纳的基本知识

一、基于xml的方式配置bean

(一)基于Xml的方式配置bean

1、执行
//1. 创建spring的ioc容器
//springXml 为spring的xml配置文件
String springXml = "spring-config.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(filePath);

//2. 从ioc容器中获取bean实例
//用id获取bean是唯一的
User user = (User) ctx.getBean("user");
//用Class获取若有多个bean会出现异常
//User user = (User) ctx.getBean(User.class);
user.setName("Spring2");
user.setAge(13);
//3. 调用实例方法
System.out.println(user.toString());
2、javabean
1)学生
//该用户
public class User {
    private String name;
    private Integer age;
    private Double mark;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public User(String name, Double mark) {
        this.name = name;
        this.mark = mark;
    }

    public User(String name, Integer age, Double mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getMark() {
        return mark;
    }

    public void setMark(Double mark) {
        this.mark = mark;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mark=" + mark +
                '}';
    }
}
2)教师
//该教师类
public class Teacher {
    private String name;
    private Integer age;
    private User user;

    public Teacher(){
    }

    public Teacher(String name, Integer age, User user) {
        this.name = name;
        this.age = age;
        this.user = user;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", user=" + user +
                '}';
    }
}
3)list集合
//集合类
public class CollectionClass {
    private List<User> userList;
    private List<Teacher> teacherList;

    public CollectionClass(){

    }

    public CollectionClass(List<User> userList, List<Teacher> teacherList) {
        this.userList = userList;
        this.teacherList = teacherList;
    }

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }

    public List<Teacher> getTeacherList() {
        return teacherList;
    }

    public void setTeacherList(List<Teacher> teacherList) {
        this.teacherList = teacherList;
    }

    @Override
    public String toString() {
        return "CollectionClass{" +
                "userList=" + userList +
                ", teacherList=" + teacherList +
                '}';
    }
}
4)map
//map类
public class MapClass {
    private Map<Teacher,User> teacherUserMap;

    public MapClass(){

    }

    public MapClass(Map<Teacher, User> teacherUserMap) {
        this.teacherUserMap = teacherUserMap;
    }

    public Map<Teacher, User> getTeacherUserMap() {
        return teacherUserMap;
    }

    public void setTeacherUserMap(Map<Teacher, User> teacherUserMap) {
        this.teacherUserMap = teacherUserMap;
    }

    @Override
    public String toString() {
        return "MapClass{" +
                "teacherUserMap=" + teacherUserMap +
                '}';
    }
}
5)properties
//properties类
public class PropertiesClass {
    private Properties properties;

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "PropertiesClass{" +
                "properties=" + properties +
                '}';
    }
}
6)peaple类
public class Peaple {
    private String name;
    private Double mark;

    public Peaple(String name, Double mark) {
        this.name = name;
        this.mark = mark;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMark() {
        return mark;
    }

    public void setMark(Double mark) {
        this.mark = mark;
    }

    @Override
    public String toString() {
        return "Peaple{" +
                "name='" + name + '\'' +
                ", mark=" + mark +
                '}';
    }
}
3、xml片段
1)通过setter配置bean中属性
<!-- 通过property标签用setter来配置bean中属性 -->   
<!-- 
	id 在xml中的唯一标识
	class 这个bean的类型
-->
<bean id="user" class="com.mySpring.test.User">
    <!-- 
		name setter修改的属性名(例如:setXxx 即 name=“xxx”)
		value 将该值赋予该属性
		ref 通过引用创建的bean来赋值
	-->
    <!-- 也可以使用其子节点ref的bean属性进行联系 -->
	<property name="name" value="Spring" ></property>
	<property name="age" value="12"></property>
</bean>
2)通过构造器配置bean中属性
<!-- 通过构造方法来配置bean中属性 -->
<!-- 使用构造器注入属性值可以指定参数的位置和参数的类型以区分重载的构造器 -->
<bean id="user2" class="com.mySpring.test.User">
    <!--
		value 表示想要通过构造函数传入的参数
		type 表示该值的类型
	-->
    <constructor-arg value="zhangsan" type="java.lang.String"></constructor-arg>
    <constructor-arg value="12" type="java.lang.Integer"></constructor-arg>
</bean>

3)配置集合属性
<!-- 为集合属性赋值 -->
<bean id="collection1" class="com.mySpring.test.CollectionClass">
    <property name="userList">
        <!-- 用list标签中的ref来引用之前已创建的bean,也可以使用内部bean创建新的bean对象(麻烦) -->
        <list>
            <ref bean="user"></ref>
            <ref bean="user2"></ref>
            <ref bean="user3"></ref>
            <ref bean="user4"></ref>
        </list>
    </property>
    <property name="teacherList">
        <list>
            <ref bean="teacher1"></ref>
            <ref bean="teacher2"></ref>
            <ref bean="teacher3"></ref>
            <ref bean="teacher4"></ref>
            <ref bean="teacher5"></ref>
        </list>
    </property>
</bean>
<!-- 配置map属性 -->
<bean id="map1" class="com.mySpring.test.MapClass">
    <property name="teacherUserMap">
        <!--
			map标签中的entry来为map赋值
				key-ref 键
				value-ref 值
		-->
        <map>
            <entry key-ref="teacher1" value-ref="user"></entry>
            <entry key-ref="teacher2" value-ref="user2"></entry>
            <entry key-ref="teacher3" value-ref="user3"></entry>
        </map>
    </property>
</bean>
<!-- 配置properties -->
<bean id="properties1" class="com.mySpring.test.PropertiesClass">
    <property name="properties">
        <props>
            <prop key="users">root</prop>
            <prop key="password">123456</prop>
            <prop key="Url">jdbc:mysql:///test</prop>
            <prop key="DriverClass">com.mysql.jdbc.Driver</prop>
        </props>
    </property>
</bean>

4、细节
1)特殊符号
<bean id="user4" class="com.mySpring.test.User">
    <!-- 特殊符号需要用<![CDATA[xxxx]]进行表示 -->
    <constructor-arg type="java.lang.String">
        <value><![CDATA[<zhangsan>]]></value>
    </constructor-arg>
    <constructor-arg value="19" type="java.lang.Integer"></constructor-arg>
    <!-- 可以用constructor-arg标签下的value进行赋值 -->
    <!-- 若参数为空即可用null标签标识 -->
    <constructor-arg type="java.lang.Double">
        <value>19.9</value>
    </constructor-arg>
</bean>

2)内部bean
<bean id="teacher3" class="com.mySpring.test.Teacher">
    <property name="name" value="lisi3"></property>
    <property name="age" value="23"></property>
    <!-- 内部bean -->
    <property name="user">
        <bean class="com.mySpring.test.User">
            <constructor-arg value="wangwu"></constructor-arg>
            <constructor-arg value="12" type="java.lang.Integer"></constructor-arg>
            <constructor-arg value="98" type="java.lang.Double"></constructor-arg>
        </bean>
    </property>
</bean>

3)级联属性赋值
<bean id="teacher5" class="com.mySpring.test.Teacher">
    <constructor-arg value="zhaoliu5"></constructor-arg>
    <constructor-arg value="29" type="java.lang.Integer"></constructor-arg>
    <constructor-arg ref="user"></constructor-arg>
    <!-- 为级联属性赋值 注意:属性要先初始化后才可以赋值 -->
    <property name="user.mark" value="11"></property>
</bean>

5)配置单独的集合bean
<!-- 配置单独的集合bean以供多个bean进行引用 -->
<util:list id="users">
    <ref bean="user"></ref>
    <ref bean="user2"></ref>
    <ref bean="user3"></ref>
    <ref bean="user4"></ref>
</util:list>
<util:list id="teachers">
    <ref bean="teacher1"></ref>
    <ref bean="teacher2"></ref>
    <ref bean="teacher3"></ref>
    <ref bean="teacher4"></ref>
    <ref bean="teacher5"></ref>
</util:list>

6)p命名空间
<!-- 通过p命名空间对属性进行赋值 -->
<bean id="collection3" class="com.mySpring.test.CollectionClass" p:userList-ref="users" p:teacherList-ref="teachers">
</bean>

7)xml自动填充属性
<!-- 通过autowire自动装配属性(用Byname时id要跟javabean风格的属性名一致才可自动装配) -->
<!-- user的bean之前的mark属性被property赋值过 -->
<bean id="teacher6" class="com.mySpring.test.Teacher"
p:name="laoliu" p:age="55" autowire="byName"></bean>

<!-- bytype跟具类型来自动装配,当有多个bean时不可用 -->
<bean id="teacher7" class="com.mySpring.test.Teacher" 
      p:name="laoqi" p:age="55" autowire="byType"></bean>

(二)bean之间的关系

    <!-- 抽象bean不能被IOC实例化,只能用来被继承配置
     若一个bean没有class属性,则该bean必须是一个抽象bean -->
    <bean id="user" class="com.mySpring.parent.User" abstract="true"
    p:name="wujinlong" p:age="15" p:mark="99"></bean>

    <!-- 通过parent来继承配置 -->
    <bean id="user2" parent="user"></bean>

    <!-- 依赖用depends-on来表示,该bean必须在有被依赖对象bean时才能使用,否则报异常 -->
    <bean id="teacher" class="com.mySpring.parent.Teacher"
          p:name="laoshiliu" p:age="53" depends-on="user2"></bean>

(三)bean作用域

<!-- 
	scope属性如果是singleton表示在IOC容器初始化时创建bean实例,IOC容器整个生命周期中只创建一个bean,单例
    prototype表示容器初始化时不创建bean实例,而在每一次请求时创建一个新的bean实例,并返回 
-->
<bean id="user" class="com.mySpring.scope.User" scope="singleton"
p:name="zuieyuyi" p:age="24" p:mark="95"></bean>

(四)使用外部文件

1)引入外部文件
<!-- 需要引入解析文件才可以使用 -->
<!-- 需context命名空间 --> 
<context:property-placeholder location="db.properties"></context:property-placeholder>

2)使用外部文件中属性
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 使用外部属性文件 ${外部文件中的属性名} -->
    <property name="user" value="${user}"></property>
    <property name="password" value="${password}"></property>
    <property name="driverClass" value="${driverclass}"></property>
    <property name="jdbcUrl" value="${jdbcurl}"></property>
</bean>

3)外部文件

数据库连接信息

user=root
password=123456
driverclass=com.mysql.cj.jdbc.Driver
jdbcurl=jdbc:mysql:///test_db?characterEncoding=utf-8&serverTimezone=UTC&rewriteBatchedStatements=true

(五)SpEL

<bean id="car" class="com.mySpring.SpEl.Car">
    <!-- 使用SpEL赋字面值 -->
    <property name="brand" value="#{'baoma'}"></property>
    <property name="price" value="#{300000}"></property>
    <!-- 使用SpEL调用静态方法 -->
    <property name="wheel" value="#{T(java.lang.Math).PI*10}"></property>
</bean>

<bean id="peaple" class="com.mySpring.SpEl.Peaple">
    <property name="name" value="zhangsan"></property>
    <!-- 使用SpEL来使用bean -->
    <property name="car" value="#{car}"></property>
    <!-- 简单的判断 -->
    <property name="city" value="#{car.price>=200000?'北京':'其他'}"></property>
</bean>

(六)bean生命周期

1)xml文件
<!--
    init-method 指定初始化方法
    destroy-method 指定销毁方法
-->
<bean id="peaple" class="com.mySpring.lifecycle.Peaple"
init-method="init" destroy-method="destroy">
    <property name="name" value="lisi"></property>
</bean>

<!--
    实现BeanPostProcessor接口,并具体提供
    Object postProcessAfterInitialization(Object bean, String beanName) :在init-method之前被调用
    Object postProcessBeforeInitialization(Object bean, String beanName):在init-method之后被调用
    的实现

    参数:
    bean:bean实例本身
    beanName:IOC容器配置的bean名字
    返回值:
    是实际上返回给用户的那个bean,注意:可以在以上两个方法中修改返回的bean,甚至返回个新的bean
-->
<!-- 配置bean的后置处理器 -->
<bean class="com.mySpring.lifecycle.MyBeanPostProcessor"></bean>

2)前置后置处理器
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     *
     * @param bean bean对象
     * @param beanName bean对象的名字
     * @return 经过后置处理后的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization:"+bean+","+beanName);
        return bean;
    }
    
    /**
     * 该方法在bean被创建之前调用
     * @param bean bean对象
     * @param beanName bean对象的名字
     * @return 经过前置处理后的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization:"+bean+","+beanName);
        return bean;
    }
}

3)运行结果
constractor.......
setname.....
postProcessBeforeInitialization:com.mySpring.lifecycle.Peaple@192b07fd,peaple
init .....
postProcessAfterInitialization:com.mySpring.lifecycle.Peaple@192b07fd,peaple
com.mySpring.lifecycle.Peaple@192b07fd
destroy .......

(七)工厂方法配置bean

1)实例工厂配置
<!-- 配置工厂实例 -->
<bean id="instanceFactory" class="com.mySpring.factory.InstanceFactory"></bean>

<!-- 通过实例工厂方法来配置bean -->
<!--
    factory-bean:指向实例工厂方法的bean
    factory-method:指向实例工厂方法的名字
    constructor-arg:如果实例工厂方法需要传入参数,则使用cansturctor-arg来配置参数
-->
<bean id="peaple2" factory-bean="instanceFactory" factory-method="getPeaple">
    <constructor-arg name="name" value="wangwu"></constructor-arg>
</bean>

//实例工厂类
public class InstanceFactory {
    private Map<String,Peaple> map = null;
    public InstanceFactory(){
        map = new HashMap<>();
        map.put("lisi",new Peaple("lisi",99.0));
        map.put("wangwu",new Peaple("wangwu",99.0));
    }

    //实例工厂方法
    public Peaple getPeaple(String name){
        return map.get(name);
    }
}

2)静态工厂类
<!-- 通过静态工厂方法来配置bean,注意:不是配置静态工方法实例,而是配置bean实例 -->
<!--
    class 属性:指向静态工厂方法的全类名
    factory-method:指向静态工厂方法的名字
    constructor-arg:如果静态工厂方法需要传入参数,则使用cansturctor-arg来配置参数
-->
<bean id="peaple1" class="com.mySpring.factory.StaticFactory"
factory-method="getPeaple">
    <constructor-arg value="lisi"></constructor-arg>
</bean>

//静态工厂类
public class StaticFactory {
    private  static Map<String,Peaple> map = new HashMap<>();

    static{
        map.put("lisi",new Peaple("lisi",99.0));
        map.put("wangwu",new Peaple("wangwu",99.0));
    }

    //静态工厂方法
    public static Peaple getPeaple(String name){
        return map.get(name);
    }

}

3)通过FactoryBean配置bean
<!-- 通过factorybean来配置bean的实例 -->
<!--
    class:指向factorybean的全类名
    property:配置factorybean的属性

    但实际返回的实例却是factorybean的getObject()方法返回的实例!
-->
<bean id="peaple" class="com.mySpring.factorybean.FactoryPeapleBean">
    <property name="name" value="lisi"></property>
</bean>

//实现FactoryBean接口
public class FactoryPeapleBean implements FactoryBean<Peaple> {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    //返回bean的对象
    @Override
    public Peaple getObject() throws Exception {
        return new Peaple(name,93.0);
    }

    /**
     * 返回bean的类型
     * @return bean的类型
     */
    @Override
    public Class<?> getObjectType() {
        return Peaple.class;
    }

    /**
     * 配置是否是一个单实例的bean
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}

二、通过注解配置bean

(一)注解

–@Component: 基本注解, 标识了一个受 Spring 管理的组件

–@Respository: 标识持久层组件

–@Service: 标识服务层(业务层)组件

–@Controller: 标识表现层组件

1)@Component注解
@Component
public class TestObject {
    public void test(){
        System.out.println("TestObject test .....");
    }
}

2)@Respository注解
@Repository("testRepository")
public class TestRepositoryImpl implements TestRepository {

    //如果可以允许这个属性不被设置需要注解@Autowired(required = false)
    @Autowired(required = false)
    private TestObject testObject;

    @Override
    public void insert() {
        System.out.println("testRepository add....");
        System.out.println(testObject);
    }
}

3)@Service注解
//@Autowired通过类型的自动填装
//当IOC容器中有多个可填装的类时,默认会报异常
//处理方式
// 1、为填装类的注释添加属性名
// 2、用@Qualifier 注释要装配的类
@Autowired
@Qualifier("testRepositoryImpl2")
private TestRepository testRepository;

public void add(){
    System.out.println("testService add ....");
    testRepository.insert();
}

4)@Controller注解
@Controller
public class TestController {
    @Autowired
    private TestService testService ;

    public void execute(){
        System.out.println("TestService execute ....");
        testService.add();
    }
}

(二)自动注入配置

<!-- 指定IOC容器扫描的包 -->
<!-- resource-pattern:只扫描满足其中指定扫描的资源 -->
<!--
<context:component-scan base-package="com.mySpring.annotation"
resource-pattern="repository/*.class"></context:component-scan>
-->

<!-- context:exclude-filter 子节点指定排除那些指定表达式的组件 -->
<!-- context:include-filter 子节点指定包含那些表达式的组件,该子节点需要use-default-filters配合使用 ______ use-default-filters="false"-->
<context:component-scan base-package="com.mySpring.annotation">
    <!-- 不包含@Repository注释的类 -->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
    <!-- 包含@Repository注释的类 -->
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
    <!-- 表示不包含这个接口以及这个接口的实现类 -->
<!--        <context:exclude-filter type="assignable" expression="com.mySpring.annotation.repository.TestRepository"/>-->
    <!-- 表示不包含这个接口以及这个接口的实现类 -->
<!--        <context:include-filter type="assignable" expression="com.mySpring.annotation.repository.TestRepository"/>-->
</context:component-scan>

(三)组件装配

1)@Autowired

•@Autowired 注解自动装配具有兼容类型的单个 Bean属性

–构造器,普通字段(即使是非public),一切具有参数的方法都可以应用@Authwired注解

–默认情况下,所有使用@Authwired注解的属性都需要被设置.当Spring找不到匹配的Bean装配属性时,会抛出异常,若某一属性允许不被设置,可以设置@Authwired注解的required属性为false

@Repository("testRepository")
public class TestRepositoryImpl implements TestRepository {

    //如果可以允许这个属性不被设置需要注解@Autowired(required = false)
    @Autowired(required = false)
    private TestObject testObject;

    @Override
    public void insert() {
        System.out.println("testRepository add....");
        System.out.println(testObject);
    }
}

2)@Qualifier

​ @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 **@**Qualifiter 已指定注入 Bean 的名称

//@Autowired通过类型的自动填装
//当IOC容器中有多个可填装的类时,默认会报异常
//处理方式
// 1、为填装类的注释添加属性名
// 2、用@Qualifier 注释要装配的类
@Autowired
@Qualifier("testRepositoryImpl2")
private TestRepository testRepository;

public void add(){
    System.out.println("testService add ....");
    testRepository.insert();
}

(四)泛型依赖注入

在这里插入图片描述

1)BaseService
public class BaseService<T> {

    @Autowired
    private BaseRepository<T> baseRepository;

    public void add(){
        System.out.println("BaseService add ...");
        System.out.println(baseRepository);
    }

}

son

@Service
public class TestService extends BaseService<User> {
}

2)BaseRepository
public class BaseRepository<T> {
}

son

@Repository
public class TestRepository extends BaseRepository<User>{
}

3)main
//spring4 泛型依赖注入
public class GenericMain {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("springGeneric-config.xml");

        TestService testService = (TestService) ctx.getBean("testService");
        //子类调用父类方法
        testService.add();
    }
}

4)结果

自动填装父类依赖对象的子类

BaseService add ...
com.mySpring.generic.TestRepository@64d7f7e0

三、AOP基础

需引入

aopalliance-1.0.jar
spring-aop-4.2.1.RELEASE.jar

(一)代理对象

1)代理对象
//实现InvocationHandler,来实现动态代理
public class PrintProxy implements InvocationHandler {
    //要有代理对象的属性
    private PrintInterface printer;

    //获取代理的对象
    public PrintInterface newProxyInstace(PrintInterface printer){
        this.printer = printer;

        //printer.getClass().getClassLoader() 代理类的类加载器
        //printer.getClass().getInterfaces() 代理类的实现接口
        //this InvocationHandler 绑定代理类的方法
        PrintInterface instance = (PrintInterface) Proxy.newProxyInstance(printer.getClass().getClassLoader(), printer.getClass().getInterfaces(), this);

        return  instance;
    }

    //代理的目的:使对象在调用方法时可以对调用进行记录通知等操作,不需要去修改类源代码
//    proxy,代理后的实例对象。
//    method,对象被调用方法。
//    args,调用时的参数。
// 返回值一般是方法产生的结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("print".equals(method.getName())){
            //前置通知
            System.out.println("start ........ start");

            try{
                Object result = method.invoke(printer, args);
                //返回通知
            }catch(Exception e){
                e.printStackTrace();
                //异常通知,可以访问到方法出现的异常
            }

            //后置通知,因为方法可能会出异常访问不到
            System.out.println("printover ........ printover");
        } else if ("write".equals(method.getName())) {
            System.out.println("start ........ start");
            method.invoke(printer,args);
            System.out.println("writeover ........ writeover");
        }
        return null;
    }
}

(二)基于注解的方式配置aop(基于AspectJ注解)

1、启动
<!-- 使AspectJ注解起作用:自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2、注解
1)切点类(@Aspect)
//把这个类声明为一个切面:需要把该类放到IOC容器中,在声明为一个切面
@Aspect
@Component
public class LoggingAspect {
}

2)切入点(@Pointcut(“execution(public int Calculator.*(int,int))”))
//定义一个用于声明恰如点的表达式,一般该该方法中不需添入其代码
//使用@Pointcut来声明切入点表达式
//后面的其他通知直接使用方法名来引用切入点表达式。
@Pointcut("execution(public int Calculator.*(int,int))")
public  void declareJoinPointExpression(){}

3)优先级(@Order(2))
//order设置优先级,值越小优先级越高
@Order(1)
@Before("LoggingAspect.declareJoinPointExpression()")
public void checkargs(JoinPoint joinPoint){
    System.out.println("--->check: " + Arrays.asList(joinPoint.getArgs()));
}

4)前置通知(@Before(“declareJoinPointExpression()”))
//声明该方法是一个前置通知:在目标方法开始之前执行
//joinpoint 表连接点类(可以通过接点类获取该方法的方法名,参数值等)
@Order(2)
@Before("declareJoinPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
    //方法名
    String methodName = joinPoint.getSignature().getName();
    //参数值
    List<Object> args = Arrays.asList(joinPoint.getArgs());
    System.out.println("the method " + methodName + " begins "+ args);

}

5)后置通知(@After(“declareJoinPointExpression()”))
//后置通知:在目标方法执行后(无论是否发生异常),执行通知。
//在后置通知中还不能访问目标方法执行的结果
@After("declareJoinPointExpression()")
public void afterMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " ends ");
}

6)返回通知(@AfterReturning(value = “declareJoinPointExpression()”,returning = “result”))
//返回通知
@AfterReturning(value = "declareJoinPointExpression()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " result " + result);
}

7)异常通知 (@AfterThrowing(value = “declareJoinPointExpression()”,throwing = “ex”))
//异常通知
@AfterThrowing(value = "declareJoinPointExpression()",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " result " + ex);
}

8)环绕通知(@Around(“execution(public int Calculator.*(int,int))”))
//环绕通知
//环绕通知需要携带 ProceedingJoinPoint 类型的参数
//环绕通知类似于动态代理的全过程:ProceedingJoinPoint 类型参数可以决定是否执行目标方法
//且必须有返回值,返回值为目标方法返回值
@Around("execution(public int Calculator.*(int,int))")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
    Object result = null;
    String methodName = proceedingJoinPoint.getSignature().getName();
    List<Object> args = Arrays.asList(proceedingJoinPoint.getArgs());

    //执行目标方法
    try {
        //前置通知
        System.out.println("the method " + methodName + " begins "+ args);

        result = proceedingJoinPoint.proceed();

        //返回通知通知
        System.out.println("The method " + methodName + " result " + result);
    } catch (Throwable e) {
        e.printStackTrace();
        //异常通知
        System.out.println("The method " + methodName + " result " + e);
    }

    //后置通知
    System.out.println("The method " + methodName + " ends ");

    return result;
}

(三)基于配置文件的方式来配置AOP

<?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-4.2.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="calculator" class="com.mytest.proxy.springaoptest.xmlaspect.CalculatorImpl"></bean>

    <bean id="loggingAspect" class="com.mytest.proxy.springaoptest.xmlaspect.LoggingAspect"></bean>
    <bean id="checkAspect" class="com.mytest.proxy.springaoptest.xmlaspect.CheckAspect"></bean>

    <!-- 配置aop -->
    <aop:config>
        <!-- 配置切点表达式
            id 唯一标识
            expression 切面表达式
         -->
        <aop:pointcut id="pointcut" expression="execution(public int com.mytest.proxy.springaoptest.xmlaspect.Calculator.*(int,int))"/>
        <!-- 配置切面及通知
            通过利用bean的实例代理器
         -->
        <aop:aspect ref="loggingAspect" order="2">
            <!-- 前置通知
                method 指定前置通知要调用的方法
                pointcut-ref 指定要应用的切面
             -->
            <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
            <!-- 后置通知 -->
            <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
            <!-- 返回通知
                returning 返回的结果给那个参数
             -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
            <!-- 异常通知
                throwing 异常的结果给那个异常
             -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"></aop:after-throwing>
        </aop:aspect>
        <aop:aspect ref="checkAspect" order="1">
            <aop:before method="checkargs" pointcut-ref="pointcut"></aop:before>
        </aop:aspect>

    </aop:config>

</beans>

四、事务

(一)使用JdbcTemplate

<!-- 利用外部配置文件 -->
<context:property-placeholder location="db.properties"></context:property-placeholder>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="driverClass" value="${jdbc.driverClass}"></property>
    <property name="jdbcUrl" value="${jdbc.jdbcurl}"></property>
</bean>

<!-- 配置Spring的 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置NamedParameterJdbcTemplate对象,该对象可使用具名参数,其中没有无参数的构造器,所以必须为其构造器指定参数 -->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

public static void singleTest(){
    //更新数据测试
    String sql = "update usertable set name=?,phone=? where id=?";
    jdbcTemplate.update(sql,"wangwu","12345678901",20);
}

public static void batchTest(){
    //批量操作
    String sql2 = "insert into usertable(name,address,phone) values(?,?,?)";
    List<Object[]> users = new ArrayList<>();

    users.add(new Object[]{"aa","huadong","12345678901"});
    users.add(new Object[]{"bb","huanan","12345678902"});
    users.add(new Object[]{"cc","xinan","12345678903"});
    users.add(new Object[]{"dd","dongnan","12345678904"});

    jdbcTemplate.batchUpdate(sql2,users);
}

public static void getObject(){
    /*
        从数据库中获取一条记录,实际得到一个对应的对象
        注意:不是调用queryForObject( String sql, Class<T> requiredType, Object... args)方法!
        而需要调用 queryForObject( String sql,RowMapper<T> rowMapper, Object... args)
        1、其中RowMapper指定如何去映射结果集的行,常用的实现类为BeanPropertyRowMapper
        2、使用sql中列的别名完成列名和类的属性名的映射,例如name lastname
        3、不支持级联属性 JdbcTemplate 到底是一个jdbc的小工具,而不是ORM框架
     */
//        String sql = "select name lastname,address lastaddress,phone lastphone from usertable where id=?";
    String sql = "select name,address,phone from usertable where id=?";
    RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
    User user = jdbcTemplate.queryForObject(sql,rowMapper,18);

    System.out.println(user);
}

public static void getObjects(){
    //从数据库中获取多条记录,实际得到一个对应的对象的集合
    String sql = "select name,address,phone from usertable where id>?";
    RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
    List<User> users = jdbcTemplate.query(sql,rowMapper,20);

    System.out.println(users);
}

public static void columentQuery(){
    //获取单个列的值或做统计查询
    String sql = "select count(id) from usertable";
    long count = jdbcTemplate.queryForObject(sql,Long.class);
    System.out.println(count);
}

public static void namedParameterJdbcTemplateTest(){
    //可以为参数起名字
    /*
        好处:
        1、若有多个参数,则不用再去对应位置,直接对应参数名,便于维护
        缺点
        1、较为麻烦
     */
    String sql = "insert into usertable(name,address,phone) values(:na,:addr,:pho)";
    Map<String,Object> map = new HashMap<>();
    map.put("na","huanshi");
    map.put("addr","fujian");
    map.put("pho","09876543211");
    namedParameterJdbcTemplate.update(sql,map);
}

public static void namedParameterJdbcTemplateTest2(){
    //使用参数时,可以使用update(String sql ,SqlParameterSource parameterSource) 进行更新操作
    /*
        1、sql 语句的参数名和类的属性名一致
        2、使用SqlParameterSource的实现类BeanPropertySqlParameterSource作为惨呼
     */
    String sql = "insert into usertable(name,address,phone) values(:name,:address,:phone)";

    User user = new User();
    user.setName("laozhao");
    user.setAddress("zhangzhou");
    user.setPhone("09876543212");

    SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(user);

    namedParameterJdbcTemplate.update(sql,sqlParameterSource);
}

(二)事务

1、注解
/**
原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.
*/
@Transactional  //事务注解

2、事务注解启动
<!-- 配置事务注解 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启动事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3、事务的传播行为
//添加事务注解
//1、使用propagation 指定事务的传播行为,即当前事务方法被其他事务方法调用时
//如何使用事务,默认取值为REQUIRED,即使用调用方法的事务
//2、使用 isolation 指定事务的隔离级别最常用的取值为 READ_COMMITTED
//3、默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以对对应的属性进行设置。通常情况下去默认值即可
//4、使用 readOnly 指定事务是否为只读。表示这个事务只读数据但不更新数据,这样可以帮助数据库引擎优化事务若真的是一个只读数据库值的方法,应设置readOnly=true
//5、使用 timeout 指定可以强制回滚之前的事务可以占用的时间
//6、*rollback* 指定回滚规则(指定,那些异常可以不会滚)

@Transactional(propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.READ_COMMITTED,
readOnly = false,
timeout = 2)
@Override
public void purchase(String isbn, String username) {
    //1、获取书的单价
    int price = bookShopDAO.findBookPriceByIsbn(isbn);

    //2、更新书的库存
    bookShopDAO.updateBookStock(isbn);

    //3、更新用户余额
    bookShopDAO.updateAccount(username,price);

}

4、Xml配置事务步骤
<!-- 1、配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 2、配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 配置要设置事务的方法
 			propagation 属性
                REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 
                REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
			isolation 属性
				隔离级别(DEFAULT(数据库默认的隔离级别),READ_UNCOMMITTED(读未提交),READ_COMMITTED(读已提交),REPEATABLE_READ(不可重复读),SERIALIZABLE(序列化))
			read-only 属性
				只读
		-->
        <tx:method name="purchase" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="false"></tx:method>
        <tx:method name="checkout" read-only="false"></tx:method>
        <tx:method name="get*" read-only="true"></tx:method>
        <tx:method name="find*" read-only="true"></tx:method>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<!-- 3、配置事务切入点,以及把事务切入点和事务属性关联起来 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut id="txPointCut" expression="execution(* com.spring.jdbc.txxml.service.impl.*.*(..))"/>
    <!-- 关联该切入点和事务属性的关联 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>

Spring IoC源码学习:总览 前言 Spring 作为现在最优秀的框架之一,被广泛的应用于 Java 项目中,但是大多数人都只知道如何使用,而不知其中的原理。对大多数人来说可能知道如何使用已经足够了,但是对于想提升自己的人来说,学习 Spring 的源码是一个不错的选择。在过去的几个月里,我利用空闲时间将 IoC 的相关源码学习了一下,按照老习惯准备整理成笔记,以博文的形式记录下来,供自己以后使用,也供其他网友参考。由于涉及... 阅读详情

相关推荐

基于 Spring Boot 的个人博客 FS-Blog

在线 Demo:http://fsblog.letec.top Github 地址:https://github.com/jameszbl/fs-blog 1. 涉及技术及工具 核心框架:SpringBoot ORM 框架:MyBatis MyBatis 工具:MyBatis Mapper MVC 框架:Spring MVC 模板引擎:Freemarker 编译辅助插件:Lombok ...

摆码王子的博客 8682

Spring Boot 学习心得和体会

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

m0_73899425的博客 557

基于Spring Boot的个人学习记录平台的设计与实现

计算机行业是一个需要其从业者终身学习的行业。在现代的计算机行业中,技术迭代越来越快,开发者需要不停的去学习新的技术,新的设计理念并应用在工作学习之中。在信息化的社会中学习的方式有很多中,视频、音频、以及各式各样的书籍文章都可以供开发者学习。但是,最高效的学习方式还是书籍,通过书籍和官方文档才可以更加细致深入地学习到新技术。如今的互联网上有许多人在写自己的技术文章,记录自己的学习笔记和感悟,并将其加以分析整理,以供他人观看学习。同时可以通过读者的反馈对其再加以修正改版,修改自己的错误。

来自一名程序猿的案例文档分享 685

spring学习笔记,初学者心得。

Spring框架0基础学生的学习笔记

qq_53049771的博客 1098

spring学习心得

本人经过大量使用和学习的总结,很经典啊

java学习感悟,对框架的学习心得

正在学习Java框架的朋友,看考一下本人学习的心得,希望对大家有用,请多多指教,我学习Java有一段时间了,对框架的理解颇有心得,希望大家一起交流下

Spring学习:学完总结

(1)全称:EJB(sun企业级开发框架)-->S(Spring)S(struts2)H(hibreate) -->S(spring)S(springmvc)M(mybatis)(2)所以Spring到底是什么?Spring不是某一层的框架, 针对各层的解决方案的框架, 提供了一站式开发, 其他框架,只需要与spring整合, Spring管理整个项目的bean(java对象), 在需要使用每个bean的时候, 找spring, 由spring把需要的bean注入给你, 每一层的解耦。1.依赖注入。

H215919719的博客 1135

Spring学习心得(一)

NULL 博文链接:https://hafuokas.iteye.com/blog/2229869

Spring 框架的学习心得

Spring 框架的学习心得 Spring 框架作为 JavaEE 开发的灵魂框架,以下是我在学习 Spring 框架时写下的一些总结和个人心得。 概述 Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供...

xkx666的博客 4978

ARM架构下的SLAM部署避坑指南:香橙派AIpro与D435i深度相机的实战解析

本文详细解析了在ARM架构的香橙派AIpro开发板上部署SLAM算法并与Intel D435i深度相机配合的实战经验。内容涵盖系统配置、ROS编译、驱动安装、算法优化等关键步骤,提供了具体的避坑指南和性能调优技巧,帮助开发者在嵌入式平台上高效实现实时SLAM应用。

f0g1h2的博客 983

Unreal Engine鸿蒙移植实战:环境配置、构建流程与性能优化指南

游戏引擎移植是跨平台开发中的关键技术环节,其核心原理在于通过适配目标操作系统的底层接口和工具链,实现同一套代码在不同硬件和软件环境下的高效运行。这项技术的价值在于最大化代码复用率,降低多平台维护成本,并能让应用快速触达新兴生态系统的用户。在移动游戏和XR应用领域,随着鸿蒙HarmonyOS生态的快速发展,将成熟的商业引擎如Unreal Engine迁移至此平台,成为开发者探索新市场机会的重要路径。本文聚焦于Unreal Engine项目向鸿蒙系统的移植实践,详细解析了从环境搭建、源码适配到构建打包的全链路流

weixin_29011395的博客 411

Spring个人深入学习路线图

一 、《Spring实战》 《Spring全家桶的深入学习(一):Spring起步》 《Spring全家桶的深入学习(二):基于SpringMVC开发web应用》 《Spring全家桶的深入学习(三):实现数据持久化》 《Spring全家桶的深入学习(四):使用Spring Data实现数据持久化》 《Spring全家桶的深入学习(五):使用Spring Security安全框架保护web应用》 《Spring全家桶的深入学习(六):Spring的配置属性》 《Spring全家桶的深入学习(七):Spri

我的博客 2837

保姆级教程:用GPU-Burn给你的NVIDIA Tesla服务器做个“压力体检”(附温度功耗监控脚本)

本文提供了一份详细的GPU服务器压力测试指南,重点介绍如何使用GPU-Burn工具对NVIDIA Tesla服务器进行全面的性能测试和温度功耗监控。从环境部署、多GPU测试策略到专业级监控指标解析,帮助运维人员构建标准化的GPU健康评估体系,确保AI训练和科学计算等关键业务的稳定性。

weixin_30619101的博客 395

Github 上 Star 最多的 Spring Boot 个人开源学习项目

2016年,在一次技术调研的过程中认识到了 Spring Boot ,试用之后便一发不可收拾的爱上它。为了防止学习之后忘记,就在网上连载了Spring Boot 系列文章,没想到这一开始便与 Spring Boot 深度结缘。 近三年的时间写了一百多篇关于 Spring Boot 的文章(包含两个课程),在写文章的过程中将文中的示例项目托管在 Github 上面,随着学习 Spring B...

纯洁的微笑 8254

TCGA数据下载保姆级教程:用GDC-client从零搞定,附配置文件避坑指南

本文提供TCGA数据下载的完整教程,详细讲解如何使用GDC-client工具从零开始配置到高效获取基因组数据。涵盖环境准备、认证配置、数据检索、高效下载及故障处理等关键步骤,并附配置文件避坑指南,帮助生物医学研究者快速掌握TCGA数据下载技巧。

weixin_30892037的博客 262

Ubuntu20.04安装NVIDIA显卡驱动避坑指南:从.run文件到完美运行

本文详细介绍了在Ubuntu20.04系统上通过.run文件安装NVIDIA显卡驱动的完整流程和避坑技巧。从禁用Nouveau驱动、选择合适显示管理器到处理常见安装问题,提供了一套经过验证的解决方案,帮助用户顺利完成驱动安装并优化性能。

weixin_30776273的博客 604

如何学习 Spring ?学习 Spring 前要学习什么?

学习完java核心技术卷一后该掌握什么知识才能学习spring? 学习完Java基础,想要继续学习JavaWeb的基础,我推荐的是下面这个路线(有帮助,记得点赞!): 一、学习Servlet和Tomcat •Servlet+Tomcat •Tomcat就是这么简单 •Servlet第一篇【介绍Servlet、HTTP协议、WEB目录结构、编写入门Servlet程序、Servlet生命周期】 •Se...

weixin_33937499的博客 1万+

基于STM32的嵌入式平台实现EtherCAT主站

本文将介绍如何在基于STM32的嵌入式平台上构建EtherCAT主站,并提供相应的源代码。使用SOEM库提供的API函数,我们可以实现与EtherCAT从站设备的实时通信和数据交换。在STM32的代码中,使用SOEM库提供的API函数初始化EtherCAT主站。在SOEM库中,我们可以找到EtherCAT主站的实现代码和相关的API函数。使用SOEM库提供的API函数实现EtherCAT主站的功能,包括发送和接收EtherCAT数据帧、处理从站设备的状态等。确保连接正常并且设备能够相互通信。

CodeSageX的博客 2834

spring学习——学完ssm之后spring笔记整理

文章目录springSpring配置其他知识Spring AOP 代码 spring Sping是一个基于IOC和AOP的结构J2EE系统的框架。 IOC反转控制是Spring的基础,Inversion Of Control。简单说就是创建对象由以前的程序员自己new构造方法来调用,变成了交由Spring创建对象。 DI依赖注入Dependency Inject,简单地说就是拿到的对象的属性,已...

qq_36730158的博客 1150

44位abogus补环境与纯算逆向分析

44位ab参数补环境与纯算加密流程逆向分析

吴秋霖的博客 2788

Python爬虫案例——解密抓取西红柿小说《我不是戏神》全章节!!!

本文介绍了抓取番茄小说网站《我不是戏神》全本内容的完整流程。首先分析网站反爬机制,发现采用自定义混淆字体加密文本内容。通过下载字体文件并解析字符映射关系,组建本地字体映射表。随后编写Python爬虫程序,实现自动获取章节链接、请求加密内容、解密替换字体、保存为本地txt文件等功能。代码包含UA伪装、请求头设置、进度条显示等细节处理,最终实现小说内容的完整抓取与解密存储。

@优快云盲敲代码的阿豪的博客 662

学习完ssm框架的项目总结

项目背景:学习完ssm背景,进行的一次项目(选课系统)实践,主要目的为了增强对ssm框架的理解,第二个是练习自己对于项目的熟悉能力,第三是为了增强自己的编程能力 1、ssm框架简单介绍: SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常...

weixin_33814685的博客 5877

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

个人学习SpringBoot 之基于spring的事件监听

个人学习SpringBoot 之spring事件监听器 首先我们自己实现一个自己的事件监听器 package com.demo.springboot.bootstrap; import com.demo.springboot.context.HelloWorldApplicationListener; import org.springframework.context.Appli...

qq_24834449的博客 270

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

个人spring学习笔记

原意:由代理人创建和管理对象,消费者通过代理人获取对象,即将对象控制权交由第三方管理,目的是降低对象间的直接耦合e.g. 去水果摊挑水果,让老板进行推荐和帮助挑选。

qq_62805168的博客 586

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Spring框架的全面详解(个人学习总结)

本文是笔者在学习和工作中使用Sping框架时所得出的一些个人理解以及学习总结,涉及到了Spring框架的两大核心技术点IOC与AOP的讲解,Spring框架中的设计模式讲解等一系列技术,本文将采用文字与代码结合的方式讲解,以此来提高大家的理解程度。

2301_77760397的博客 3393
下一篇: mybatis参数处理
magicproblem
博客等级 码龄7年 22粉丝 59原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值