02-SSM框架开发

2023-12-21  本文已影响0人  努力学习的lfk

2023-12-22


1、Java原生开发存在问题

1.1、DAO层存在问题


1)每次都要做大量的相同的操作
2)对执行sql语句过程中所出现的各种异常和资源释放进行处理
3)需要更多的代码来提取结果并将它们映射到对象实例中

1.2、Service层存在问题


1)当功能越来越多, 与业务逻辑无关, 但是被多处业务逻辑模块共享的代码(比如判断用户登录, 日志管理, 权限检查, 事务管理等)修改和维护的成本大大增加
2)没有做到资源复用,创建了许多重复对象。造成大量资源浪费*
3)更换实现类需要改动多个地方。修改和维护的成本大大增加。

参考CSDN博主「灵魂相契的树」的原创文章,原文链接:https://blog.csdn.net/yzhcjl_/article/details/132520053
参考知乎作者「码农出击」的原创文章,原文链接:https://zhuanlan.zhihu.com/p/493973685

1.3、Controller层存在问题


1)每个servlet处理特定的请求,如果servlet过多,会导致在web.xml内容过多(web.xml文件方式配置)、servlet类文件数量过多。
2)通常使用JSP进行页面展示,对前端不是特别友好。
3)servlet具有容器依赖性,必须放在服务器中运行,不利于单元测试。JSP 页面无法直接访问需要有 Java Servlet 容器来编译和运行

2、解决方案

SSM框架开发.png
SpringMVC
1)
2)
Spring
1)Spring IOC有许多优点,它可以帮助改善代码的结构、可维护性和可测试性。低耦合——代码分离、便于代码复用
2)Spring AOP能帮助我们无耦合的实现日志记录,性能统计,安全控制等。高内聚——结构清晰、便于开发维护

Mybatis
1)避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。resultType自动映射查询结果,resultMap复杂场景下构建一个结果映射

参考CSDN博主「向上爬的小蜗牛」的原创文章,原文链接:https://blog.csdn.net/LyySwx/article/details/90900013


3、Spring

3.1、IOC

IoC(inversion of control):把对象的创建、赋值、管理工作都交给代码之外的容器实现,即对象的创建是由其他外部资源完成。

相关概念
1)控制:创建对象,对象的属性赋值,对象之间的关系管理。
2)反转:把原来的开发人员管理、创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象、创建对象、给属性赋值。
3)正转:由开发人员在代码中,使用new构造方法创建对象等,开发人员主动管理对象。

/*
示例:
Spring IoC 容器创建好 B 的实例对象后并赋值给 A 对象中的 b 属性(成员变量)的过程,就是所谓的「依赖注入
*/
public class A {
    private B b;
    // 省略 getter 和 setter
    // 省略构造方法
}

IoC的技术实现:
DI (Dependency Injection,依赖注入):是IoC 的一种具体实现方式,它是指将对象所依赖的其他对象(即依赖)通过构造函数、Setter 方法或其他方式注入到对象中,从而消除了对象之间的耦合关系。
只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建、赋值、查找都由容器内部实现

3.1.1、依赖注入的方式

1)基于构造方法的依赖注入

构造方法的优势: 保证一些必要的属性在Bean实例化时就得到设置,并且确保了Bean实例在实例化后就可以使用。
构造方法的弊端: 在创建对象时,如果用不到这些数据,也必须提供。

标签:<constructor-arg></constructor-arg>
属性:

type:用于指定要注入的数据类型,该数据类型也是构造函数中的某些类型或者某些参数
index:指定要注入的数据给构造函数中指定索引位置的参数赋值,参数索引的位置从0开始
name:用于指定构造函数中指定名称的参数赋值(常用)
(以上三个标签根据需要选一个就行)
一一一一一一一一一一一一一一一一一
value:用于提供基本类型和String类型的数据
ref:用于指定其他Bean类型的数据,需填写在SpringIOC容器中的唯一id

//基于构造方法注入
public class UserDao {
    private String name;
    private int age;

    public UserDao(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void insert(){
        System.out.println("保存用户:"+age+"岁"+name);
    }
}
//基于构造方法注入
public class UserService {
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add(){
        userDao.insert();
    }
}
    <!--按名称匹配入参-->
    <bean id="userDao" class="org.example.dao.UserDao">
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="age" value="55"></constructor-arg>
    </bean>
    <bean id="userService" class="org.example.service.UserService">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

Ⅱ、按类型匹配入参

    <!--按类型匹配入参-->
    <bean id="userDao" class="org.example.dao.UserDao">
        <constructor-arg type="java.lang.String" value="李四"></constructor-arg>
        <constructor-arg type="int" value="55"></constructor-arg>
    </bean>
    <bean id="userService" class="org.example.service.UserService">
        <constructor-arg type="org.example.dao.UserDao" ref="userDao"></constructor-arg>
    </bean>

Ⅲ、按索引匹配入参

    <!--按索引匹配入参-->
    <bean id="userDao" class="org.example.dao.UserDao">
        <constructor-arg index="0" value="李四"></constructor-arg>
        <constructor-arg index="1" value="55"></constructor-arg>
    </bean>
    <bean id="userService" class="org.example.service.UserService">
        <constructor-arg index="0" ref="userDao"></constructor-arg>
    </bean>

Ⅳ、联合使用类型和索引匹配入参(适用于有多个构造函数,根据索引入参无法匹配的情况)

    <!--联合使用类型和索引匹配入参-->
    <bean id="userDao" class="org.example.dao.UserDao">
        <constructor-arg index="0" type="java.lang.String" value="李四"></constructor-arg>
        <constructor-arg index="1" type="int" value="55"></constructor-arg>
    </bean>
    <bean id="userService" class="org.example.service.UserService">
        <constructor-arg index="0" type="org.example.dao.UserDao" ref="userDao"></constructor-arg>
    </bean>
//基于构造方法注入
    @Test
    public  void  test(){
        //读取配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
        //获取bean的实例
        UserService userService =(UserService) ctx.getBean("userService");
        //调用方法
        userService.add();
    }

2)基于 Setter 的依赖注入

set方法的优势: 创建对象时没有明确的限制,可以直接使用默认构造函数,具有可选择性和灵活性高的优点。
set方法的弊端: 如果有某个成员必须有值的时候,则获取对象时有可能set方法无法保证一定注入。

标签:<property></property>
属性:

name:用于指定注入时调用的set方法名
value:用于提供基本类型和String类型的数据
ref:用于指定其他Bean类型的数据,需填写在SpringIOC容器中的唯一id

//基于set方法注入
public class UserDao {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public void insert(){
        System.out.println("保存用户:"+age+"岁"+name);
    }
}
//基于setter方法注入
public class UserService {
    private UserDao userDao;

    public void add(){
        userDao.insert();
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
<!--基于setter方法注入-->
    <bean id="userDao" class="org.example.dao.UserDao">
        <property name="name" value="张三"></property>
        <property name="age" value="15"></property>
    </bean>
    <bean id="userService" class="org.example.service.UserService">
        <property name="userDao" ref="userDao"></property>
    </bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config.xml")
public class UserServiceTest {
    //基于setter方法注入
    @Test
    public  void  test(){
        //读取配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
        //获取bean的实例
        UserService userService =(UserService) ctx.getBean("userService");
        //调用方法
        userService.add();
    }
}


3)基于 注解 的依赖注入

使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。

//基于注解
@Repository
public class UserDao {
    @Value("王五")
    private String name;
    @Value("15")
    private int age;

    public void insert(){
        System.out.println("保存用户:"+age+"岁"+name);
    }
}

//等价于XML配置中的
<bean id="userDao" class="org.example.dao.UserDao">
    <constructor-arg name="name" value="王五"></constructor-arg>
    <constructor-arg name="age" value="15"></constructor-arg>
</bean>
@Service
public class UserService {

//    @Autowired
//    private UserDao userDao;

//    @Autowired
//    @Qualifier("userDao")
//    private UserDao userDao;

    @Resource(name = "userDao")
    private UserDao userDao;

    public void add(){
        userDao.insert();
    }
}

//等价于XML配置中的
<bean id="userService" class="org.example.service.UserService">
     <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

以上三种写法都可

    //基于注解
    @Autowired
    private UserService userService;

    @Test
    public  void  test1(){
        userService.add();
    }

Spring中常见注解
用于创建对象的注解

注解的作用和 xml 配置文件中编写 <bean></bean> 标签的功能是一样的,即将当前类对象存入 Spring 容器中

用于改变创建的对象的注入数据的注解
用于改变创建的对象的作用范围的注解
生命周期相关的注解
Spring的新注解
// 如果加载spring-context.xml文件:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");

// @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
@PropertySource(value={"classpath:jdbc.properties"})
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
 
    @Bean(name="dataSource")
    public DataSource createDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
@Configuration
@ComponentScan(basePackages="pers.stringbug")
@PropertySource(value={"classpath:jdbc.properties"})
@Import(value={JdbcConfig.class})
public class SpringConfiguration {
    /**
     * 创建 QueryRunner 对象,并存入 Spring 容器中
     * 其中 @Qualifier(value="dataSource") 指定 bean 参数 id
     */
    @Bean(name="runner")
    public QueryRunner createQueryRunner(@Qualifier(value="dataSource") DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
}

3.1.2、依赖注入的类型

1)注入bean类型

示例如上

2)注入基本数据类型

示例如上

3)给List结构注入:List、Set集合,Array数组

List集合需要使用<list>标签来配置注入
Set集合需要使用<set>标签来配置注入,其配置参数及含义和<list>标签完全一样。
Array数组需要使用<array>标签来配置注入,其配置参数及含义和<list>标签完全一样。

public class School {
    private List<String> studentList;
    private Set<String> studentSet;
    private String[] studentArray;
    //省略setter、getter方法
}
<!--  测试setter注入复杂类型  -->
    <bean id="school" class="org.example.entity.School">
        <property name="studentList">
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
            </list>
        </property>

        <property name="studentSet">
            <set>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
            </set>
        </property>

        <property name="studentArray">
            <array>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
            </array>
        </property>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config.xml")
public class UserServiceTest {
    //测试基于setter注入复杂数据类型
    @Test
    public  void  test(){
        //读取配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
        //获取bean的实例
        School school =(School) ctx.getBean("school");
        //List结构注入
        System.out.println(school.getStudentList());
        System.out.println(school.getStudentSet());
        System.out.println(Arrays.toString(school.getStudentArray()));
        //Map结构注入
//        System.out.println(school.getStudentAgeMap());
//        System.out.println(school.getStudentClassProperties());
    }
}
4)给Map结构注入:Map集合,Properties对象

Map集合需要使用<map>标签来配置注入,其属性“key-type”和“value-type”分别指定“键”和“值”的数据类型。
Properties对象需要使用<props>标签来配置注入,键和值类型必须是String,不能变,子标签<prop key=”键”>值</prop>来指定键值对。

public class School {
    private Map<String,Integer> studentAgeMap;
    private Properties studentClassProperties;
    //省略setter、getter方法
}
<!--  测试setter注入复杂类型  -->
    <bean id="school" class="org.example.entity.School">
        <property name="studentAgeMap">
            <map>
                <entry key="张三" value="18"></entry>
                <entry key="王五">
                    <value>16</value>
                </entry>
            </map>
        </property>

        <property name="studentClassProperties">
            <props>
                <prop key="张三">初一三班</prop>
                <prop key="李四">初二五班</prop>
            </props>
        </property>
    </bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config.xml")
public class UserServiceTest {
    //测试基于setter注入复杂数据类型
    @Test
    public  void  test(){
        //读取配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-config.xml");
        //获取bean的实例
        School school =(School) ctx.getBean("school");
        //Map结构注入
        System.out.println(school.getStudentAgeMap());
        System.out.println(school.getStudentClassProperties());
    }
}

参考知乎作者「god23bin」的原创文章,原文链接:https://zhuanlan.zhihu.com/p/640517982
参考CSDN博主「ADAMs.」的原创文章,原文链接:https://blog.csdn.net/qq_43097201/article/details/104486887
参考博客园作者「LeeHua」的原创文章,原文链接:https://www.cnblogs.com/liyihua/p/14482488.html


3.2、AOP

简言之,就是在【编码时】把业务逻辑处理代码(例如:用户信息管理的业务逻辑功能)和额外的功能代码(日志记录,权限控制,事务处理等)【分离】开来,降低程序间的耦合度,使得软件的结构更加清晰

在【运行时】,使用【动态代理技术】把业务逻辑处理和额外功能处理【结合】到一块,既能完成业务逻辑处理,也能完成额外的功能。并且额外功能逻辑可以多次重复使用,减少了重复代码的开发,提高了程序的复用性

实现方式:

实现方式

实现方式

实现方式

动态代理

3.2.1、JDK动态代理

3.2.2、CGLIB动态代理


4、Mybatis


5、SpringMVC

上一篇下一篇

猜你喜欢

热点阅读